Julia: новый синтаксис для транспонирования

Созданный на 15 мар. 2017  ·  103Комментарии  ·  Источник: JuliaLang/julia

Теперь, когда .op обычно является векторизованной формой op , очень сбивает с толку то, что .' означает транспонирование, а не векторизованную форму ' (присоединенное, также известное как ctranspose ). Этот выпуск предназначен для обсуждения альтернативных синтаксисов для транспонирования и/или сопряжения.

linear algebra parser

Самый полезный комментарий

Категорически против того, чтобы транспонировать матрицу tr(A) — все будут думать, что это означает трассировку матрицы: https://en.wikipedia.org/wiki/Trace_ (linear_алгебра)

Все 103 Комментарий

Андреас попробовал Aᵀ (и, возможно, Aᴴ ) в #19344, но это не было очень хорошо принято. Аналогичным образом мы могли бы каламбурить на ^ со специальными типами экспонент T (и, возможно, H ), так что A^T является транспонированным, но это тоже довольно сомнительно. Не уверен, что есть много других хороших вариантов, которые все еще немного похожи на математическую нотацию.

Я вроде как думаю, что t(A) может быть лучшим, но прискорбно «украсть» другое однобуквенное имя.

Перемещение моего комментария из другой проблемы (не то чтобы это что-то решает, но...):

+1 за использование чего-то другого, кроме .' .

Я не смог найти языки со специальным синтаксисом для транспонирования, за исключением APL, в котором используется не столь очевидный , и Python, в котором используется *X (что могло бы сбить Джулию с толку). Несколько языков используют transpose(X) ; R использует t(X) . Это некрасиво, но не хуже, чем .' . По крайней мере, у вас меньше соблазна использовать ' , путая его с .' : было бы ясно, что это очень разные операции.

См. код Розетты . (Кстати, пример Джулии на самом деле иллюстрирует сопряженное транспонирование...)

Можно ли использовать один из других тиков? ` или "

-100 к изменению сопряженного, так как это одна из замечательных вещей, которая делает написание кода Julia таким же ясным, как написание математики, плюс сопряженное транспонирование обычно является тем, что вам нужно в любом случае, поэтому имеет смысл иметь для него сокращенный синтаксис.

Пока у нас есть приятный синтаксис для сопряженного транспонирования, постфиксный оператор для обычного транспонирования кажется в основном ненужным, поэтому просто наличие обычного вызова функции мне кажется вполне приемлемым. transpose уже работает; мы не могли просто использовать это? Я нахожу t(x) R-изм неудачным, так как из названия неясно, что он на самом деле должен делать.

Использование другой галочки было бы несколько странным, например, A` может выглядеть очень похоже A' в зависимости от шрифта, а A" слишком похоже на A'' .

Если мы внесем изменение в #20978, то транспонирование постфикса на самом деле станет более полезным, чем сейчас. например, если у вас есть два вектора x и y , и вы хотите попарно применить к ним f , вы можете сделать, например, f.(x, y.') ... с # 20978 , это будет применимо к массивам произвольных типов.

Честно говоря, я думаю, что наш лучший вариант — оставить все как есть. Ни одно из предложений не кажется мне явным улучшением. Преимущество .' в том, что он знаком с Matlab. . на самом деле несколько соответствует синтаксису точечного вызова в таких примерах, как f.(x, y.') , и предполагает (несколько правильно), что транспонирование «сливается» (оно не создает временную копию благодаря RowVector и их будущие обобщения).

На самом деле, мы могли бы пойти еще дальше и сделать f.(x, g.(y).') операцией слияния. т.е. мы меняем транспонирование .' на нерекурсивное аля #20978 и расширяем его семантику, чтобы включить слияние с другими вложенными вызовами точки. (Если вам нужна версия без слияния, вы должны позвонить transpose .)

Мне очень нравится этот план, @stevengj.

Одна загвоздка: предположительно макрос @. не превращает y' в y.' (поскольку это было бы неправильно). Однако это может превратить y' в какую-то плавно-сопряженную операцию.

Основная проблема заключается в том, чтобы найти чистый способ заставить f.(x, g.(y).') иметь плавящую семантику. Одной из возможностей было бы преобразовать его в f.(x, g.(y.')) и, следовательно, в broadcast(x,y -> f(x, g(y)), x, y.') ?

Обратите внимание, что для того, чтобы это работало правильно, нам может потребоваться восстановить резервный метод transpose(x) = x , и в этом случае мы могли бы также оставить транспонирование оставаться рекурсивным.

Я думаю, что решение о том, должно ли транспонирование быть рекурсивным или нет, ортогонально тому, заставляем ли мы его участвовать в слиянии точечного синтаксиса. Выбор сделать его нерекурсивным не мотивирован этим.

@StefanKarpinski , если восстановить резервный вариант transpose(x) = x , то большая часть мотивации изменить его на нерекурсивный исчезнет.

В чем проблема, если запасной вариант восстановлен, но у нас все еще есть нерекурсивное транспонирование?

@jebej рекурсивное транспонирование более правильно, когда оно используется как математическая операция над линейными операторами. Если я правильно помню, основная причина сделать его нерекурсивным заключалась в том, что нам не нужно определять резервный вариант transpose(x) = x , а не выдавать MethodError.

Но было бы не страшно иметь запасной вариант, но при этом быть нерекурсивным.

Позвольте добавить два комментария (я просмотрел предыдущее обсуждение и не заметил их - извините, если что-то упустил):

  • документация для permutedims говорит , что это обобщение транспонирования для многомерных массивов. transpose , что на самом деле не так.
  • Как предполагается транспонировать вектор x=["a", "b"] ? На самом деле y=x.' работает и создает новую переменную, но getindex не работает. Насколько я знаю, вам нужно использовать reshape(x, 1, :) или намного медленнее hcat(x...) , чтобы достичь этого, но неестественно иметь другой синтаксис для Vector ( permutedims здесь не работает ).

Каков ваш вариант использования для переноса вектора строк?

Например, рассмотрим следующий сценарий:

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

и я хочу добавить y после последней строки x . И самый простой способ — vcat транспонировать y .

Возникает на практике при инкрементальной записи текстовых данных в Matrix{String} (я мог бы использовать Vector{Vector{String}} ), но часто матричный оказывается более полезным (или тогда опять возникает вопрос, как преобразовать Vector{Vector{String}} до Matrix{String} путем вертикального объединения последовательных элементов).

Другой вариант использования: транспонирование — это самый простой способ сделать два вектора ортогональными друг другу, чтобы передать функцию через декартово произведение ( f.(v, w.') ).

Точка данных: вчера я столкнулся с участником, сбитым с толку постфиксным оператором «широковещательно-прилегающий» и почему он ведет себя как транспонирование. Лучший!

FWIW, я твердо убежден, что нам следует избавиться от синтаксиса .' . Как человек, более знакомый с Julia, чем с Matlab, я ожидал, что это будет означать векторизованное сопряжение, и я был действительно сбит с толку, когда это не так. Julia не является Matlab и не должна быть связана соглашениями Matlab - если в Julia точка означает векторизацию смежной функции, то это должно быть согласовано во всем языке и не должно иметь случайного ужасного исключения, которое .' формально не имеет отношения к ' .

Я думаю, что вполне нормально иметь transpose без каких-либо специальных обозначений «галочек», поскольку в подавляющем большинстве случаев оно вызывается для матрицы действительных чисел, поэтому ' будет эквивалентно, если вы действительно хочу сохранить набор текста. Если мы хотим сделать совмещенную версию транспонирования, то я действительно не думаю, что .' — это правильный синтаксис.

Неплохо подмечено. Возможно, только сопряженному нужен суперкомпактный синтаксис.

Давайте просто назовем это transpose и объявим устаревшим .' . В будущем мы можем подумать, хотим ли мы, чтобы .' было точечно сопряженным, или мы просто хотим оставить его навсегда устаревшим, чтобы не заманивать в ловушку пользователей Matlab.

Обратите внимание, что я только что проверил зарегистрированные пакеты и нашел более 600 случаев использования .' , так что это не так уж редко. А с точечными вызовами / broadcast (которые только в 0.6 начали полноценно обрабатывать нечисловые данные) желание лениво транспонировать нечисловые массивы (где adjoint имеет меньше смысла), вероятно, станет гораздо более распространенным, так что аргумент в пользу компактного синтаксиса несколько усиливается.

Тогда нам лучше отказаться .' как можно скорее, прежде чем больше кода попадет в ловушку плохого шаблона использования.

Почему это плохо?

Проблема в том, что .' теперь не означает то, что кажется оператором с точкой.

Как я уже сказал выше, потому что это нарушает общий шаблон, согласно которому . означает векторизацию и выглядит так, как будто это означает векторизованное сопряжение (особенно для тех, кто не знаком с Matlab).

Я думаю, что @stevengj делает хорошее замечание — это связано с желанием простого нерекурсивного транспонирования.

Я знаю, что это было непопулярным, но я начинаю отдавать предпочтение Андреасу #19344 за . На данный момент я бы предпочел отказаться от использования _всех_ надстрочных индексов в качестве идентификаторов и интерпретировать _любые_ завершающие надстрочные индексы как постфиксные операторы. Это также дает путь к разрешению некоторой неразберихи вокруг literal_pow с использованием надстрочных чисел. Да, было бы грустно потерять χ² и такие имена переменных, но я думаю, что преимущества перевешивают недостатки.

На данный момент я бы предпочел отказаться от использования _всех_ надстрочных индексов в качестве идентификаторов и интерпретировать _любые_ завершающие надстрочные индексы как постфиксные операторы.

RIP мой код
screenshot from 2017-11-09 22-08-25

На данный момент я бы предпочел отказаться от использования всех надстрочных индексов в качестве идентификаторов.

Я действительно не думаю, что это было бы необходимо, когда мы просто хотим T и, возможно, пару других вещей в будущем.

Глупая консистенция…

Да, использование .' для транспонирования несколько непоследовательно, но все предложенные альтернативы кажутся хуже. Это не самое худшее, что можно сказать в мире: « .' транспонируется, что является исключением из обычного правила, касающегося точечных операторов». Вы учитесь этому и идете дальше.

Следует отметить одну вещь, которая может помочь с любой потенциальной путаницей по поводу того, что .' не является точечной трансляцией, заключается в том, что это постфиксный оператор, тогда как префиксная трансляция — op. , а инфиксная — .op . Таким образом, мы можем сказать, что . не означает широковещательную рассылку, когда это постфикс. Другое использование постфикса . — это поиск по полю, а getfield(x, ') не имеет смысла, поэтому он отличается от других значений.

(Тем не менее, я предпочитаю transpose(x) сохранению .' .)

@stevengj Могу поспорить, что многие (возможно, большинство) из 600+ случаев использования .' в зарегистрированных пакетах, о которых вы упомянули выше, могут быть заменены на ' без ущерба для удобочитаемости, а код продолжал бы работать.

Возможно, это не популярно, но могут ли быть постфиксы " и ` ?

использование .' в зарегистрированных пакетах, которые вы упомянули выше, можно было бы заменить на ' без ущерба для читабельности, и код продолжал бы работать.

Обратите внимание, что как только #23424 приземлится, мы сможем использовать transpose для массивов строк и так далее, но не adjoint . Лучшей практикой использования линейной алгебры x.' , скорее всего, станет что-то вроде conj(x') (надеюсь, это лениво, т.е. бесплатно). Хотя мне нравится использовать .' из-за его компактности, возможно, избавление от него заставит пользователей линейной алгебры использовать правильную вещь, а пользователей массивов данных использовать прописанное transpose .

может быть еще постфикс " и `?

Новый синтаксис для transpose() кажется преждевременным. ИМХО, было бы лучше просто отказаться .' и заменить его, как вы предлагаете, на conj(x') и transpose по мере необходимости.

У меня есть ощущение, что .' настолько полезен в Matlab, главным образом из-за того, что Matlab настаивает на том, что «все является матрицей», а также из-за отсутствия согласованных правил нарезки, поэтому вам часто нужно вставлять случайные транспонирования в разных местах, чтобы заставить вещи работать.

Подводя итог аргументам здесь:

  1. .' теперь является единственным выдающимся оператором с точкой, который не означает «применять оператор без точек поэлементно»; новые пользователи, не пришедшие из Matlab, находят это неожиданной ловушкой.

  2. .' теперь фактически неоднозначно: вы имели в виду transpose или вы имели в виду conj(x') ? В принципе, каждое устаревшее использование .' должно быть проверено, чтобы определить, переставляет ли оно индексы двумерного массива или выполняет «несопряженное сопряжение».

Первая проблема проблематична, но не фатальна сама по себе; вторая проблема действительно плохая – это уже не единая связная операция, а скорее она будет расщеплена на два отдельных значения.

Я только что заметил, что если мы когда-нибудь изменим .' на «элементарно сопряженный», то conj(x') будет примерно эквивалентно x'.' , а conj(x)' будет примерно равно x.'' , что очень близко к x.' 😬.

Возможно, это не популярно, но все же может быть постфикс «и `?

Скопируйте и вставьте код в Slack, и вы увидите, что подсветка синтаксиса уничтожения будет...

Возможность транспонировать что угодно хороша, потому что это упрощает «перекрестное произведение» с помощью механизма диспетчеризации и других кратких кратких вариантов использования, подобных этому. Проблема с отсутствием простого запасного варианта для такого рода вещей заключается в том, что неизменно хак, который мы увидим, состоит в том, чтобы просто определить резервные варианты transpose(x) = x (или на базовых типах, поэтому пиратство типов в пакетах), чтобы сделать это такая вещь работает легко. Это заставляет меня задуматься: почему Complex не лишний? Примыкание к большинству чисел есть само по себе, поэтому примыкание к комплексу — это то, на чем следует специализироваться: разве это не может быть расширено за пределы чисел?

Я вижу здесь две очень связанные вещи:

1) x' не работает для нечисловых типов, поэтому нам нужен способ легко сделать это для других данных
2) transpose(x) не так прост, как x.' . В основном это относится к случаям (1), поскольку случаи использования для транспонирования сложных матриц гораздо реже.

Но вместо того, чтобы снижаться (2), почему бы не попытаться сделать разумное исправление для (1)?

Может быть, разумное исправление — это просто макрос, который делает ' означающим транспонирование вместо примыкания?

Но вместо того, чтобы снижаться (2), почему бы не попытаться сделать разумное исправление для (1)?

Мы уже прошли этот путь и несколько рядом с ним. В результате было проведено большое количество дискуссий, которые, возможно, кто-то еще сможет отфильтровать, но в целом это не сработало. По сути, математическая операция adjoint не имеет смысла для объектов, не являющихся числами. Использование ' для нечисел только потому, что вам нравится краткий синтаксис, плохо — это наихудший вид каламбура оператора, и неудивительно, что из такого злоупотребления значением вытекают плохие вещи. Функция adjoint должна быть определена только для вещей, с которыми имеет смысл брать сопряжение, а ' следует использовать только для этого.

Помните, что .' в том виде, в каком он используется в настоящее время, — это принципиально две разные операции: транспозиция массива и несопряженное сопряжение. Проблема рекурсивного транспонирования подчеркивает тот факт, что это разные операции и поэтому нам нужны разные способы их выражения. Математики кажутся непреклонными в том, что несопряженная сопряженная операция (а) важна и (б) отличается от простой замены измерений местами. В частности, чтобы быть правильным, несопряженное сопряженное должно быть рекурсивным. С другой стороны, замена размеров универсального массива явно не должна быть рекурсивной. Таким образом, эти операции должны быть записаны по-разному, а существующие варианты использования .' должны быть устранены неоднозначно как имеющие то или иное значение. Устаревание .' — это способ добиться этого.

Наконец, хотя я твердо уверен, что permutedims(x, (2, 1)) определенно слишком неудобен для замены размерностей двумерного массива, я нахожу аргумент, что transpose(x) слишком неудобен, неубедительным. Является ли эта операция настолько распространенной, что простое и понятное имя для нее — это слишком? Действительно? Является ли замена размеров массива более распространенной или важной, чем все остальные вещи в языке, для которых мы используем имена функций и синтаксис вызова функций? Обозначение хаусхолдера делает adjoint совершенно особенным, поскольку мы хотим писать такие вещи, как v'v , v*v' и v'A*v . Вот почему adjoint имеет действительно хороший синтаксис. Но поменять местами размеры массива? На мой взгляд, это не гарантирует оператора.

Не сильный аргумент, но я часто использую оператор ' для печати более компактных массивов (при использовании в качестве простых контейнеров), например, когда я хочу видеть содержимое нескольких векторов одновременно на моем экране ( и неизменно расстраиваюсь, когда это терпит неудачу, потому что элементы не могут быть перемещены). Так что короткий синтаксис для REPL определенно удобен. (Кроме того, это облегчает людям, привыкшим к массивам строк, иметь простой способ «переключить порядок», в частности, при переносе алгоритмов на julia с использованием 2d-массивов; но, безусловно, это тоже не сильный аргумент). Просто скажу, что это хороший краткий синтаксис, который полезен не только для линейных алгебраистов.

Я прокомментировал некоторые синтаксические идеи на https://github.com/JuliaLang/julia/pull/19344#issuecomment -261621763, в основном это было так:

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

Но без хака, конечно, просто идея о том, что может быть своего рода «приложение постфиксной функции» и что оно требует круглых скобок (x)f , версии с точками могут быть такими (x).f ( xf будет идентификатором, даже если f является надстрочным символом).

Этот пример хака работал на 0.6, но теперь:

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            

Что печально, я изначально хотел сделать это для полномочий:

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 "³"

Что, как я наивно полагал, активирует такой синтаксис, как: n² = (n)² и n³ = (n)³ Но любой числовой идентификатор запрещен на первой позиции, однако (A)⁻¹ также работает, где ⁻¹ было const ⁻¹ = inv .

Я реализовал аналогичный хак для InfixFunctions.jl .

Как пользователь, я мог бы просто сделать пакет PostfixFunctions.jl и быть довольным тем, что вы найдете здесь лучшим. Но в настоящее время это синтаксические ограничения:

  • использование числовых супериндексов в начале идентификатора не допускается
  • супериндекс x * ᶠ в постфиксе (неявное умножение в хаке) (x)ᶠ не допускается

Мне кажется, ИМХО, это слишком много, я хотел бы, по крайней мере, иметь возможность определять идентификаторы, которые могут начинаться с числовых надстрочных индексов, или, в более общем смысле, запрещать только фактические числовые символы 0-9 с числовой семантикой в ​​начале идентификатор, это было бы здорово. 😄

Ваше здоровье!

См. #10762 для обсуждения других числовых символов в качестве идентификаторов.

Другая проблема связана с #22089, операторские суффиксы. +ᵀ теперь является допустимым оператором, который (вероятно, случайно) запрещал идентификаторы, состоящие только из комбинации символов, в контекстах, где можно было бы ожидать оператора. Мне это кажется ошибкой. Также немного странно, что является допустимым идентификатором, а -ᵀ не делает -(ᵀ) . Однако это не конец света, и исправление IMO не стоило бы потери других возможных вариантов использования .

Обратите внимание, что использование .' в качестве оператора транспонирования постфикса здесь даже не обсуждается (несмотря на то, что говорится в теме проблемы), на самом деле соображение заключается в том, следует ли нам сохранить .' в качестве оператора постфикса. для несопряженного сопряженного, которое было бы рекурсивным. Это часто бывает таким же, как транспозиция, но обычно это не та же самая операция. Если люди, занимающиеся линейной алгеброй, хотят, чтобы .' означало транспонирование универсального массива, это другая история, но у меня сложилось впечатление, что это неприемлемо.

@ Ismael-VC, я вижу, что (x)ᵀ можно использовать в качестве синтаксиса постфиксной функции для надстрочных индексов - ведь что еще это может означать? Я думаю, что ваше предложение начинает раздражать людей неправильным образом, позволяя применять любой идентификатор как функцию в синтаксисе постфикса. Я бы ограничил это верхними индексами.

@StefanKarpinski , я думал, что консенсус состоял именно в том, чтобы разрешить .' означать нерекурсивную, несопряженную транспозицию массива (если у нас вообще есть этот оператор), в то время как ' является рекурсивной, сопряженной сопряженная операция.

Я очень, очень ненавижу идею использования для оператора транспонирования постфикса. Слишком полезно использовать надстрочный индекс в именах переменных , например aᵀa или LᵀDL = ltdlfact(A) . (Помимо того факта, что использование только для оператора, в то время как другие верхние индексы допустимы в идентификаторах, было бы странно.)

Это было совсем не мое понимание — я думал, что linalg сторонники сохранения a.' как есть, т.е. означающего conj(a)' . Сохранение .' , но изменение его значения на транспонирование массива - это совсем другое - я не уверен, как я к этому отношусь. Я согласен, что иметь только в качестве постфиксного оператора было бы раздражающим и непоследовательным. Однако мне больше нравится предложение @Ismael-VC (a)ᵀ , которое не мешает использовать aᵀ в качестве имени.

Мои воспоминания об этих дискуссиях отражают воспоминания Стивена. Рекурсивное, не сопряженное транспонирование встречается редко и, как правило, довольно странно. Достойное резюме здесь: https://github.com/JuliaLang/julia/issues/20978#issuecomment -316141984.

Я думаю, мы все согласны с тем, что постфикс ' является смежным и должен оставаться.
Думаю, мы все согласны с тем, что постфикс .' — неоптимальный синтаксис.
Я думаю, что большинство согласны с тем, что нерекурсивная (структурная) транспонирование более полезна, чем рекурсивное транспонирование.

Итак, пункты, с которыми все согласны:

  1. Используйте a' для adjoint(a)
  2. Используйте conj(a)' или conj(a') для (не)сопряженного сопряжения.

Таким образом, единственный спорный момент заключается в том, как записать транспонирование массива:

  • Как a.' или
  • Как transpose(a) или
  • Как (a)ᵀ .

Верна ли эта оценка?

Да, я так думаю (где «транспонирование массива» нерекурсивно).

Кроме того, насколько я понимаю, все согласны с тем, что transpose(a) определенно должен быть допустимым синтаксисом (и нерекурсивным), и единственные точки разногласий заключаются в том, является ли .' и/или (a)ᵀ должен быть альтернативным (полностью эквивалентным) допустимым синтаксисом.

Подход (1) из https://github.com/JuliaLang/julia/issues/20978#issuecomment -315902532, который получил значительную поддержку (например, https://github.com/JuliaLang/julia/issues/20978# issuecomment-316080448), остается возможность. У меня есть ветка, реализующая этот подход (представляющая flip(A) ), которую я могу опубликовать.

Что бы это ни стоило, я поддерживаю отказ .' . Путаница и двусмысленность в этой теме сами по себе являются сильным аргументом в пользу этого. Лучший!

Я считаю, что пока у нас есть постфикс ' , люди захотят использовать его для трансляции f по декартовому произведению векторов с f.(v, w') . И люди захотят использовать его для преобразования вектора строк в вектор-строку заголовков для табличной структуры. Поэтому для меня важно иметь простую и удобную в использовании замену, к которой мы можем их направить.

Вот вариант, который мы не рассмотрели: A*' — новый биграф. Типичная математическая запись может интерпретировать это как conj(A)' , что на самом деле довольно близко к тому, что нам нужно. Это было доступно в версии 0.6, но в версии 0.7 мы разрешаем использовать * для объединения символов… хотя это все еще работает.

Я не верю, что постфиксы " и ` доступны из-за синтаксического анализа пользовательских строковых литералов за пределами конца строки. Postfix * сам по себе также недоступен по той же причине. Postfix prime A′ , вероятно, является одним из наиболее часто используемых идентификаторов Юникода, так что это даже больше, чем Aᵀ .

Честно говоря, после просмотра моего кода я вообще не использую .' , так что transpose(a) , вероятно, подойдет.

Обратите внимание, что я только что проверил зарегистрированные пакеты и нашел более 600 случаев использования .', так что это не так уж редко.

Было ли вообще проверено это место, чтобы увидеть, не использовалось ли .' там, где ' было бы в порядке? Я начинаю думать, что это может быть правдой чаще, чем нет. В противном случае единственное место, где я видел законное использование .' , было до того, как метки Plots.jl разрешали вектор (вместо этого требовался вектор-строка строк), но это было изменено. Для кодов, где мне это действительно нужно часто, я думаю, что начал бы делать T = transpose локально или бросал макрос, чтобы изменить ' на transpose .

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

со мной было бы хорошо для этого редкого случая.

люди захотят использовать его для трансляции f по декартовому произведению векторов с f.(v, w'). И люди захотят использовать его для преобразования вектора строк в вектор-строку заголовков для табличной структуры. Поэтому для меня важно иметь простую и удобную в использовании замену, к которой мы можем их направить.

Если он появляется в операторе только один раз, разве нельзя просто использовать transpose ?

Синтаксис a*' для conjugate-adjoint довольно хорош, хотя на самом деле не похоже, что для этой операции нам нужен лучший синтаксис. &a скоро будет доступен и предлагает поменять местами вещи, хотя это сильно отличается от традиционных обозначений для этого.

Может быть, пришло время для соломенного опроса?

Как правильно писать структурное транспонирование?

(примерно в порядке предложения; здесь нет суждений об именах эмодзи)

  • 👍: A.' — просто измените значение, оставьте синтаксис прежним
  • 👎: transpose(A) — без специального синтаксиса
  • 😄: t(A) или tr(A) — без специального синтаксиса, но экспортировать более короткое имя
  • 🎉: Aᵀ — всего и, возможно, один или два надстрочных индекса в специальном регистре от идентификаторов.
  • 😕: (A)ᵀ — все верхние индексы отделены от идентификаторов и ведут себя как постфиксные операторы.
  • ❤️: A*' — замазать эту сверхъестественную долину, это означает структурное транспонирование
  • Если вы предпочитаете &A , отметьте 🎉 пост Стефана выше (у нас закончились смайлики)

В обсуждениях LinAlg действительно говорилось о передаче .’ нерекурсивному транспонированному использованию, поскольку conj(x’) встречается относительно редко. Однако является математическим синтаксисом и действительно должен иметь математическое значение (во всяком случае).

Категорически против того, чтобы транспонировать матрицу tr(A) — все будут думать, что это означает трассировку матрицы: https://en.wikipedia.org/wiki/Trace_ (linear_алгебра)

Если не отказаться от использования надстрочных индексов в качестве идентификатора (что, вероятно, следует серьезно рассматривать до версии 1.0), то ᵀ(A) тоже возможно.

Что касается предложения (A)ᵀ , приношу свои извинения за то, что немного отвлек это обсуждение следующим замечанием:

Меня никогда особо не заботило наличие в качестве унарного оператора, тем более, что вам все равно придется вводить √(...) , как только вы захотите применить его к переменной, которая больше, чем один или несколько символов. Кроме того, я всегда находил разницу в функционировании между и √a очень искусственной. Вероятно, это имеет смысл, если вы знаете о классах Unicode и т. Д., Но для всех остальных это должно показаться абсурдным. Конечно, полезно иметь в качестве действительного имени переменной, но аналогично √a может быть полезным именем переменной для хранения квадратного корня из a , если вам нужно использовать его несколько раз. раз. Или более сложные выражения, такие как a²b и его квадратный корень a√b , где первое является допустимым идентификатором, а второе — нет. Больше всего мне нравится постоянство.

Поэтому для согласованности мне нравится предложение иметь постфиксные операторы при использовании круглых скобок (A)ᵀ , (a)² в сочетании с удалением унарного оператора Unicode (и его родственников), чтобы его также можно использовать в идентификаторах (хотя он по-прежнему доступен как обычный вызов функции √(a) ).

Я согласен на 100% с тем, что сказал @Jutho , и думал об этом несколько раз. Не могли бы вы открыть вопрос, @Jutho? Предложение: разрешить в именах идентификаторов, требовать √(x) для вызова как op.

следующий вопрос -> как насчет 2 |> √ ?

Давайте обсудим в другой ветке, но вкратце 2 |> √ означает √(2) .

Другой альтернативой, которая не требует изменений в синтаксическом анализаторе и которую легко набирать, может быть A^T для транспонирования (определением T как одноэлементного типа с методом ^ ). … о, я вижу, у @mbauman тоже была такая идея. Это немного некрасиво, но не более A.' .

У меня нет опыта, но я очень заинтересован в результатах этого обсуждения, поскольку в процессе моей работы, скорее всего, мне придется набирать тысячи строк, содержащих матричные выражения.

transpose(A) # with no special syntax побеждает в голосовании выше, но это больно для моих глаз и пальцев.

В python обычное использование, вероятно, связано с numpy и множеством вещей, которые выглядят следующим образом и не так уж плохи:

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)

Я бы не хотел делать:

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

Это полностью меняет ментальную концепцию транспонирования по сравнению с соглашением, на котором остановилась математическая нотация, которая представляет собой постфиксный знак, обычно Aᵀ или Aᵗ .

Лично я очень доволен A' , который работает в Julia v.0.6, до того, как его забрали adjoint. Часто ли используется adjoint?

Вот мои комментарии в таблице:

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.

Лично я очень доволен A' , который работает в Julia v.0.6, до того, как его забрали adjoint. Часто ли используется adjoint?

Я не понимаю, A' всегда был сопряжен с A . Раньше мы называли базовую функцию ctranspose для сопряженного транспонирования, но мы переименовали ее в эквивалентный термин adjoint без изменения функциональности.

Если вы занимаетесь линейной алгеброй, то вам, скорее всего, понадобится сопряженное транспонирование, поэтому вы будете вводить A' , а не transpose(A) . Популярность отказа от определения специального синтаксиса для несопряженных транспонирований (предположительно) отчасти объясняется тем фактом, что для большинства линейных алгебраических применений не так уж часто требуется несопряженное транспонирование.

Если вы занимаетесь линейной алгеброй, то...

Если ваш инструмент молоток, то... :)

... вы должны подумать о возможности того, что Джулия может вырасти до общего языка программирования.

Может быть, нет, может быть, это останется арго линейной алгебры - это возможность, о которой должны думать такие программисты, как я. :)

@mahiki , ты пример NumPy:

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)

будет написано буквально в Джулии как:

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

или предполагая, что векторы являются строками в этом примере NumPy и будут столбцами в Julia:

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

который кажется настолько ясным и математическим, насколько это возможно. Если ваши данные реальны, то сопряжение и транспонирование — это одна и та же операция, возможно, поэтому вы используете транспонирование выше, но математически сопряжение — это правильная операция. Как сказал @ararslan , X' всегда означало adjoint в Джулии (и в Matlab тоже). Ранее он назывался ctranspose , сокращенно от « сопряженное транспонирование », но это имя было неправильным, поскольку определяющим свойством оператора является то, что

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

которое является определяющим свойством эрмитова сопряженного , но, как оказалось, удовлетворяется транспонированным сопряжением, когда A является комплексной матрицей. Вот почему «сопряженный» — правильный общий термин для этого оператора.

При этом я проголосовал выше как за transpose(a) , так и a.' , поскольку я думаю, что было бы нормально, если бы a.' означало структурное транспонирование. Он будет работать так, как ожидалось, и даже если он не будет рекурсивным и, следовательно, не «математически правильным» в каком-то общем коде, его работа, как ожидается, кажется достаточно хорошей. И совет людям подумать об использовании conj(a') в универсальном коде кажется образовательным, а не чем-то, что нам действительно нужно, чтобы ударить людей по голове.

@mahiki Если по какой-то причине вам действительно нужно много раз использовать transpose вместо adjoint в вашем коде, вы можете определить более короткий макрос, например @t , который использует псевдоним transpose (хотя я знаю, что это решение не идеально, особенно если вы пишете свой код с другими людьми).

вы должны подумать о возможности того, что Джулия может вырасти до общего языка программирования.

@ Liso77 Это уже так. В качестве одного из многих примеров Nanosoldier запускает веб-сервер, который прослушивает события GitHub и запускает тесты производительности по запросу, и все это в Джулии. Это отступление, и я не хочу, чтобы эта тема отклонялась от темы.

Если вы транспонируете какую-либо матрицу с нечисловыми данными — что является вполне допустимым вариантом использования — нотация математического транспонирования на самом деле кажется плохой игрой слов. В этом случае я чувствую, что было бы лучше более четко указать, что вы просите, например, transpose (или даже permutedims , в зависимости от ваших конкретных потребностей).

Если вы транспонируете какую-либо матрицу с нечисловыми данными — что является вполне допустимым вариантом использования — нотация математического транспонирования на самом деле кажется плохой игрой слов.

Поскольку A.' на самом деле не является «математической транспонированной записью» в обычном смысле, я не вижу, что это аргумент за или против.

Я думаю, что @ararslan выступает не против существующего .' , а скорее против введения синтаксиса верхнего индекса-T. Я склонен согласиться - если вы имеете в виду концепцию сопряжения линейной алгебры, то вам следует использовать ' (даже если ваша матрица окажется реальной). И если у вас есть матрица нечисловых данных, то, конечно, вполне законно переставить два индекса местами, но эта операция на самом деле не является «транспонированием», как мы обычно о ней думаем, и использование математической нотации верхнего индекса-T является вероятно, скорее запутать, чем прояснить. Единственная ситуация, когда запись надстрочного индекса-T действительно уместна, — это если у вас есть числовая матрица, индексы которой вы хотите переставить, но вам действительно не нужен сопряженный линейный оператор. Такие ситуации, безусловно, существуют, но могут быть слишком редкими, чтобы оправдать введение нового синтаксиса.

... но эта операция на самом деле не является "транспонированием", как мы обычно о ней думаем, ...

Если это так необычно, почему ararslan и многие другие голосуют за структурное транспонирование правописания как transpose(A) ?

Спасибо @StefanKarpinski @ararslan @ttparker. Мне пришлось вернуться к моему тексту по линейной алгебре и заново открыть для себя сопряженное, оно там в порядке. Я взял это перед Комплексным анализом, возможно, поэтому я не обратил на это внимания.

мне нравится уметь это делать
gradient = (1/m) * (X'Θ - Y) * X'

Мое замешательство связано с широко распространенным использованием слова «транспонировать» (как надстрочный индекс T) в справочных документах, статьях, учебниках и т. д., например , в конспектах лекций Эндрю Нг в Стэнфорде CS229 , где соответствующий код Джулии будет использовать adjoint как в чистом примере @StefanKarpinski выше. Это потому, что присоединенные и транспонированные эквивалентны в ℝ (правильно?) . обновление: да

Теперь моя любимая нотация для транспонирования — это просто то, что логически непротиворечиво. Ясно, что .' не из-за конфликта с синтаксисом оператора с точками, и я не возражаю против transpose(A) без специального синтаксиса, поскольку кажется, что никакой разумный специальный синтаксис не доступен, за исключением надстрочного индекса Unicode.

Мне нравится решение @ttparker , если я нахожу, что пишу много транспонированных макросов @t с псевдонимами transpose .

Опять же, я ошибся, заявив:

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

Спасибо, что серьезно отнеслись к моим комментариям, несмотря на то, что я плохо разбираюсь в математике на уровне выпускников.

(Из речи .)

Я хотел бы, чтобы ' был постфиксным оператором, который отображает f' в '(f) , где Base.:'(x::AbstractMatrix) = adjoint(x) и пользователь может добавлять другие методы, которые имеют ничего общего с соседями. (Например, некоторым людям может понравиться f' для ссылки на df/dt.)

С суффиксами операторов, введенными в версии 0.7, было бы естественно, чтобы f'ᵃ сопоставлялся с 'ᵃ(f) и т. д., позволяя пользователю определять свои собственные постфиксные операторы. Это позволило бы иметь Base.:'ᵀ(x::AbstractMatrix) = transpose(x) и Base.:'⁻¹(x::Union{AbstractMatrix,Number}) = inv(x) и т. д.

Написание A'ᵀ , возможно, не так чисто, как Aᵀ , но это не потребует отказа от имен переменных, оканчивающихся на .

На первый взгляд кажется, что это неразрывная функция. Это очень умный компромисс. Мне это нравится.

Мне кажется разумным. Самое сложное — это придумать имя для функции ' — синтаксис префикса в этом случае не работает.

Самое сложное — это придумать имя для функции

apostrophe ? Может слишком буквально...

Можно ли вообще заставить работать синтаксис префикса (например, с явным синтаксисом (')(A) ?)? Если нет, то это проблема, поскольку это нарушит правило if-you-can-define-the-symbol-name-then-you-can-override-its-syntax, введенное https://github.com/JuliaLang .

Изменить: выглядит доступным:

julia> (')(A)


ERROR: syntax: incomplete: invalid character literal

julia> (')(A) = 2


ERROR: syntax: incomplete: invalid character literal

К сожалению, ' — один из самых сложных символов для использования в качестве имени идентификатора, поскольку он вводит другой тип атома (символов), который имеет очень высокий приоритет (равный приоритету самих идентификаторов). Например, является ли (')' приложением ' к самому себе или открытой скобкой, за которой следует литерал ')' ?

Один вариант, который нецелесообразен в краткосрочной перспективе, состоит в том, чтобы объявить символьные литералы не стоящими ' и вместо этого использовать строковый макрос, такой как c"_" .

Как насчет того, чтобы ' анализировалось как идентификатор, если ему предшествует точка-двоеточие, чтобы работало Base.:' ?

Конечно, (@__MODULE__).:'(x) = function_body может быть немного громоздким при написании, но (x)' = function_body должно работать так же. Изменить: нет, так как (x)' должен сопоставляться с вызовом ' в Base . Определение функции ' в текущем модуле было бы громоздким, но и не было бы никакой причины делать это.

Или как насчет того, чтобы разрешить синтаксический анализ '' как идентификатора ' , когда в противном случае он был бы проанализирован как литерал пустого символа (что в настоящее время является ошибкой уровня синтаксического анализа). Точно так же ''ᵃ будет интерпретироваться как идентификатор 'ᵃ и т. д.

Все, что в настоящее время не является синтаксической ошибкой, будет по-прежнему анализироваться, как и раньше (например, 2'' — это постфикс ' , примененный дважды к 2 ), но 2*'' теперь будет анализировать как два раза ' .

Кажется сбивающим с толку, что у нас будет a'' === a , но ''(a) === a' . Вместо этого лучше использовать Base.apostrophe в качестве имени (или что-то в этом роде).

Может быть, было бы лучше разделить это обсуждение на новую проблему Github, поскольку речь идет о синтаксисе ' , который напрямую не связан с транспозицией матриц?

Есть ли автоматический способ разделения вопросов, или я должен просто открыть новый и дать ссылку на обсуждение здесь?

Последний

Единственная ситуация, когда запись надстрочного индекса-T действительно уместна, — это если у вас есть числовая матрица, индексы которой вы хотите переставить, но вам действительно не нужен сопряженный линейный оператор. Такие ситуации, безусловно, существуют, но могут быть слишком редкими, чтобы оправдать введение нового синтаксиса.

Думаю, я слишком опоздал для обсуждения, но я хотел бы указать на одно использование, которое, я думаю, стоит упомянуть: применение дифференцирования сложного шага к функции с действительным знаком, которая имеет transpose внутри Это. (Лично я понял, что именно по этой причине мне нужны .' в MATLAB и julia.)

Я приведу пример с несколькими вхождениями transpose (может быть, я мог бы избежать этого?)

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

Дело в том, что f и Df должны быть определены с помощью transpose и не должны использовать сопряжение.

Я не думаю, что комплексный ступенчатый метод супер актуален для юлии. Разве это не изящный хак/обходной путь для автоматического дифференцирования в случаях, когда язык поддерживает эффективные встроенные комплексные числа, но не может быть определен эквивалентно эффективный числовой тип Dual ? Это не относится к julia, у которой действительно хорошие библиотеки автоматической дифференциации.

Я согласен с использованием двойных чисел вместо метода сложных шагов, и это очень хорошее замечание, которое вы делаете (лично я уже заменил все свои оценки метода сложных шагов на двойные числа в julia). Тем не менее, я думаю, что это все еще допустимый вариант использования для демонстрационных целей, обучения трюкам (см., например, Ник Хайэм, рассказывающий о методе сложных шагов на Julia Con 2018 ) и переносимости (другими словами, я беспокоюсь, что Версия кода MATLAB выше, использующая комплексные числа, была бы чище).

Выходя из мира инженеров и, возможно, физиков, которые используют сложные массивы больше, чем настоящие массивы, отсутствие оператора транспонирования - это немного больно. (Комплексное векторное представление для гармонической зависимости от времени повсеместно используется в нашей области.) Лично я предпочел бы простой синтаксис xH и xT, хотя мое единственное соображение — это краткость.

Плотность оператора транспонирования относительно эрмитова транспонирования в моем коде составляет примерно 1 к 1. Так что несопряженная транспонирование одинаково важна для меня. Во многом транспонирование используется для создания внешних продуктов и правильного размера массивов для взаимодействия с другим кодом или для умножения матриц.

На данный момент я намерен просто предоставить макрос или функцию одного символа для операции, однако что является правильным эквивалентом старой функциональности, transpose() или permutedims()?

transpose предназначен для линейной алгебры и является рекурсивным, а permutedims — для нерекурсивного расположения данных любого типа.

Интересно, вы говорите, что используете транспонирование так же, как и сопряжение. Раньше я был таким же, но в основном потому, что я имел тенденцию делать ошибки там, где мои данные были реальными, поэтому я имел тенденцию транспонировать, но на самом деле сопряженная операция была правильной операцией (обобщенная на сложный случай - сопряженная операция была правильной операцией для моего алгоритма). Конечно, есть (много) действительных исключений.

Во всем, что связано с электродинамикой, вы часто используете пространственноподобные векторы и хотите использовать векторные операции в R^n (обычно n=3), то есть, в частности, transpose , даже если ваши векторы являются комплексными, потому что вы сделали преобразование Фурье. Кажется, @mattcbro говорит о таких приложениях.

При этом, читая обсуждения синтаксиса, я часто размышляю о том, что лично я не мог себе представить, что несколько более подробный синтаксис замедляет скорость или эффективность моего программирования. Обдумывание самого алгоритма и наиболее естественного/эффективного способа его реализации занимает гораздо больше времени.

Во всем, что связано с электродинамикой, вы часто используете пространственно-подобные векторы и хотите использовать векторные операции в R ^ n (обычно n = 3), то есть, в частности, транспонировать, даже если ваши векторы имеют комплексные значения, потому что вы взяли Фурье. трансформировать.

Не обязательно. Часто вам нужны средние по времени величины из амплитуд Фурье, и в этом случае вы используете комплексное скалярное произведение, например, ½ℜ[𝐄*×𝐇] — это средний по времени поток Пойнтинга из комплексных компонентов Фурье, а ¼ε₀|𝐄|² — это средняя во времени плотность энергии вакуума. С другой стороны, поскольку оператор Максвелла (обычно) является комплексно-симметричным оператором («обратным»), вы часто используете несопряженный «скалярный продукт» для (бесконечномерной) алгебры на полях 𝐄(𝐱) и т. д. над все пространство.

Это правда, у меня было слово часто в первом предложении, но я его, видимо, убрал :-).

Ну, если вы хотите пойти туда, электромагнитные величины еще более сжато записаны в алгебраической формулировке Клиффорда, часто называемой геометрической алгеброй. Эти алгебры имеют несколько автоморфизмов и антиавтоморфизмов, которые играют решающую роль в формулировке теории, особенно при рассмотрении задач рассеяния.

Эти алгебры обычно имеют краткое матричное представление, и эти морфизмы часто легко вычисляются с помощью комплексного транспонирования, эрмитова транспонирования и сопряжения.

Тем не менее, как я уже говорил ранее, мое основное использование транспонирования часто состоит в том, чтобы упорядочить мои массивы для взаимодействия с другими массивами, другим кодом и заставить матричное умножение работать с правильным размером сглаженного массива.

Лично я бы предпочел синтаксис numpy для xH и xT.

Легко реализовать сейчас в 1.0 и должно быть эффективным:

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 

Это на удивление легко и аккуратно. Недостатком является то, что ортогональное использование getproperty не сочетается друг с другом. Таким образом, любой, кто реализует getproperty для своего конкретного типа матрицы, должен будет реализовать общее поведение вручную.

ортогональное использование getproperty не составляет

Хм. Интересно, означает ли это, что xT «должен был» быть снижен до getproperty(x, Val(:T)) . Я содрогаюсь при мысли о том, что это может сделать с бедным компилятором.

Я уверен, что у каждого есть свое мнение, но для меня это почти особенность, что трудно построить общий интерфейс из точечного синтаксиса. Не поймите меня неправильно, это действительно замечательная функция, прекрасно подходящая для определения именованных кортежеподобных структур и так далее.

(Также можно довольно легко добавить слой отправки Val к вашим типам).

Код @ c42f работает как шарм. К сожалению для меня, я пытаюсь написать код, который работает в версиях 0.64 и выше, что вынуждает меня использовать либо транспонирование, либо мою собственную определенную функцию T(A) = transpose(A). Возможно, макрос был бы немного чище и эффективнее.

Чтобы было ясно, я не предлагаю определить этот конкретный getproperty как хорошую идею для пользовательского кода. Скорее всего, это сломает ситуацию в долгосрочной перспективе ;-) Хотя, возможно, однажды мы достаточно хорошо прочувствуем последствия, чтобы мы могли определить x.T в Base .

Но в общем смысле мне интересно, почему такое использование свойства для определения «геттеров» в универсальных интерфейсах на самом деле плохо. Например, общие функции получения полей в настоящее время имеют колоссальную проблему с пространством имен, которая просто решается разумным использованием getproperty . Гораздо приятнее написать x.A , чем написать MyModule.A(x) , какое-нибудь более длинное уродливое имя функции, такое как get_my_A(x) , или экспортировать чрезвычайно общее имя A от пользователя. модуль. Единственная проблема, на мой взгляд, заключается в ожидаемой способности переопределять значение .B для подтипов независимо от того, что .A определено в общем для супертипа. Отсюда полусерьезный комментарий про Val .

Забавная идея:

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

Персонаж выглядит как T , но на самом деле это ' с полосой над ним. Не уверен, что серьезно...

screen shot 2018-09-10 at 11 29 56

Да, мне тоже так кажется на GitHub. Но это перебор. Скопируйте и вставьте в мой терминал показывает:

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

Слишком умный и милый. Тем не менее, мне по-прежнему нравятся комбинированные символы, и я думаю, что 'ᵀ — это хорошо.

-100 к изменению сопряженного, так как это одна из замечательных вещей, которая делает написание кода Julia таким же ясным, как написание математики, плюс сопряженное транспонирование обычно является тем, что вам нужно в любом случае, поэтому имеет смысл иметь для него сокращенный синтаксис.

В подобном заявлении есть некоторая наглость. Учтите, что некоторая конечная часть разработчиков явно _не_ хочет_ adjoint() , но _нужна_ transpose() .

Случай и точка для нас, работающих с символическими вычислениями для моделирования оператора ' по умолчанию, может привести, например, к ошибочному возврату псевдообратной формы (A'*A)\(A *b) или квадратичной формы v'*A*v длинные и сложные результаты, которые нельзя уменьшить.

Возможно, решением является какая-то директива компилятора, объявляющая значение ' .

Была ли эта страница полезной?
0 / 5 - 0 рейтинги