Julia: 转置的新语法

创建于 2017-03-15  ·  103评论  ·  资料来源: JuliaLang/julia

现在.op通常是op的矢量化形式, .'表示转置而不是'的矢量化形式(伴随,又名 ctranspose )。 此问题用于讨论转置和/或伴随的替代语法。

linear algebra parser

最有用的评论

非常强烈反对使tr(A)表示矩阵转置 - 每个人都会认为这意味着矩阵跟踪: https ://en.wikipedia.org/wiki/Trace_ (linear_algebra)

所有103条评论

Andreas 在 #19344 中尝试Aᵀ (也许还有Aᴴ ),但它并没有受到很好的欢迎。 我们可以类似地用特殊的指数类型T (可能还有H )对^进行双关语,这样A^T是转置的,但这也相当可疑。 不确定还有很多其他的好选择,它们仍然有点/有点像数学符号。

我有点认为t(A)可能是最好的,但不幸的是“窃取”了另一个单字母名称。

将我的评论从另一个问题移开(不是说它解决了任何问题,而是......):

+1 用于使用.'以外的东西。

除了使用不太明显的的 APL 和使用*X的 Python (这会让 Julia 感到困惑)之外,我找不到具有特殊语法的语言用于转置。 几种语言使用transpose(X) ; R 使用t(X) 。 这并不漂亮,但并不比.'差。 至少您不太想通过将' $7$#$ 与.'混淆来使用它:很明显,这些是非常不同的操作。

请参阅罗塞塔代码。 (顺便说一句,朱莉娅的例子实际上说明了共轭转置......)

可以使用其他刻度之一吗? `"

-100 改变伴随,因为它是让编写 Julia 代码和编写数学一样清晰的令人敬畏的事情之一,而且共轭转置通常是你想要的,所以有一个缩写的语法是有意义的。

只要我们有良好的共轭转置语法,用于常规转置的后缀运算符似乎几乎没有必要,因此将其作为常规函数调用对我来说似乎很好。 transpose已经有效; 我们不能就用那个吗? 我发现t(x) R-ism 很不幸,因为从名称中并不清楚它实际上应该做什么。

使用不同的刻度会有点奇怪,例如A`可能看起来很像A' ,具体取决于字体,而A"看起来太像A'' .

如果我们在 #20978 中进行更改,那么后缀转置实际上变得比现在更有用。 例如,如果您有两个向量xy并且您想在它们上成对应用f ,您可以使用 #20978 执行例如f.(x, y.') ... ,这将适用于任意类型的数组。

老实说,我认为我们最好的选择仍然是保持原样。 这些建议对我来说似乎都不是一个明显的改进。 .'的优点是熟悉 Matlab。 .实际上与f.(x, y.')之类的示例中的点调用语法有些一致,并暗示(有些正确)转置“融合”(由于RowVector它不会产生临时副本

事实上,我们甚至可以更进一步,让f.(x, g.(y).')成为一个熔断操作。 即,我们将.'转置更改为非递归 ala #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失败了。 AFAIK 你必须使用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]

我想在 x 的最后一行之后附加y x 。 最简单的方法是vcat转置y

在将文本数据增量记录到Matrix{String}时出现在实践中(我可以使用Vector{Vector{String}} ),但通常矩阵更有用(或者再次存在如何转换Vector{Vector{String}}的问题Matrix{String}通过垂直连接连续元素)。

另一个用例:转置是使两个向量相互正交以便在笛卡尔积 ( f.(v, w.') ) 上广播函数的最简单方法。

数据点:昨天我遇到了一个被后缀“broadcast-adjoint”操作符弄糊涂的人,以及为什么它表现得像转置。 最好的!

FWIW,我强烈认为我们应该摆脱.'语法。 作为一个对 Julia 比对 Matlab 更熟悉的人,我预计它是指矢量化伴随,而当它没有时,我真的被绊倒了。 Julia 不是 Matlab,不应该受 Matlab 约定的约束 - 如果在 Julia 中,点表示相邻函数的矢量化,那么这应该在整个语言中保持一致,并且不应该随机出现.'的可怕异常'无关。

我认为transpose没有任何特殊的“刻度”符号就可以了,因为绝大多数时候,它是在实数矩阵上调用的,所以'如果你真的想节省打字。 如果我们想制作一个融合版本的转置,那么我真的不认为.'是正确的语法。

那是个很好的观点。 可以说只有伴随词需要超紧凑的语法。

让我们称之为transpose并弃用.' 。 将来,我们可以考虑是否要将.'作为逐点伴随,或者我们是否只想将其永久弃用以避免陷入 Matlab 用户。

请注意,我刚刚 grepped 注册的包,发现.'有 600 多种用法,所以这并不是非常罕见。 并且使用点调用/ broadcast (仅在 0.6 中才开始完全处理非数字数据),懒惰转置非数字数组(伴随的意义不大)的愿望可能会变得更加普遍,所以紧凑语法的论点有所加强。

然后我们最好尽快弃用.' ,以免更多代码陷入糟糕的使用模式。

为什么不好?

问题是.'现在并不意味着它看起来像点运算符。

正如我上面所说,因为它违反了.表示矢量化的一般模式,并且看起来它意味着矢量化伴随(尤其是对于不熟悉 Matlab 的人)。

我认为@stevengj提出了一个很好的观点——这与对简单非递归转置的渴望有关。

我知道它不受欢迎,但我开始以的价格支持 Andreas 的 #19344。 在这一点上,我倾向于反对使用 _all_ 上标作为标识符,并将 _any_ 尾随上标解释为后缀运算符。 这也为使用上标数字解决literal_pow周围的一些混乱提供了途径。 是的,失去χ²之类的变量名会很遗憾,但我认为好处大于坏处。

在这一点上,我倾向于反对使用 _all_ 上标作为标识符,并将 _any_ 尾随上标解释为后缀运算符。

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函数应该只定义在有意义的事物上,并且'应该只用来表示那个。

请记住,当前使用的.'基本上是两种不同的操作:数组转置和非共轭伴随。 递归转置问题突出了这样一个事实,即这些是不同的操作,因此我们需要不同的方式来表达它们。 数学家似乎坚持认为,非共轭伴随运算(a)很重要,并且(b)与简单的维度交换不同。 特别是,为了正确,非共轭伴随应该是递归的。 另一方面,交换通用数组的维度显然不应该是递归的。 因此,这些操作需要以不同的方式编写,并且.'的现有用法需要消除歧义,因为它们具有一种或另一种含义。 弃用.'是一种强制执行此操作的方法。

最后,虽然我强烈认为permutedims(x, (2, 1))对于交换 2d 数组的维度肯定太不方便,但我发现transpose(x)太不方便的论点没有说服力。 这个操作是不是太常见了,以至于有一个简单、清晰的函数名是不是太过分了? 真的吗? 与我们使用函数名称和函数调用语法的语言中的所有其他事物相比,交换数组维度是否更常见或更重要? Householder 表示法确实使adjoint非常特别,因为我们想要编写诸如v'vv*v'v'A*v类的东西。 这就是为什么adjoint获得非常好的语法的原因。 但是交换数组的尺寸? 在我看来,它不保证运营商。

不是一个强有力的论点,但我经常使用'运算符来打印更紧凑的数组(当用作简单容器时),例如当我想在我的屏幕上同时查看几个向量的内容时(并且在失败时总是会感到沮丧,因为元素无法转置)。 因此,REPL 的简短语法绝对是方便的。 (此外,这使得习惯于行主要数组的人们更容易有一种简单的方法来“切换顺序”,特别是在使用 2d 数组将算法移植到 julia 时;但也绝对不是一个强有力的论据)。 只是说这是一个很好的简洁语法,它不仅对线性代数有用。

我在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

但是,当然,如果没有 hack,只是认为可以有各种“后缀函数应用程序”并且它需要括号(x)f ,虚线版本可能是这样的(x).f ( xf将是一个标识符,即使f是一个上标符号)。

这个示例 hack 曾经在 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实现了类似的 hack。

作为一个用户,我可以只做一个PostfixFunctions.jl包,并对你在这里找到的最好的东西感到满意。 但目前这种语法限制:

  • 不允许在标识符的开头使用数字超索引
  • 后缀中的超级索引x * ᶠ (hack 中的隐式乘法) (x)ᶠ不允许

恕我直言,对我来说似乎有点太多了,我希望至少能够定义可以以数字上标开头的标识符,或者更一般地说,只在开头禁止具有数字语义的实际数字字符 0-9标识符,那太棒了。 😄

干杯!

有关其他数字字符作为标识符的一些讨论,请参见#10762。

另一个问题与#22089,运算符后缀有关。 +ᵀ现在是一个有效的运算符,它(可能是偶然的)不允许在可能需要运算符的上下文中仅由组合字符组成的标识符。 这对我来说似乎是一个错误。 是有效标识符但-ᵀ不这样做也有点奇怪-(ᵀ) 。 然而,这不是世界末日,IMO 修复它不值得失去的其他可能用途。

请注意,使用.'作为后缀转置运算符甚至不在此处讨论(尽管问题的主题是这样说的),实际上考虑的是我们是否应该将.'保留为后缀运算符对于非共轭伴随,这将是递归的。 这恰好经常与转置相同,但通常不是相同的操作。 如果线性代数的人愿意让.'表示通用数组转置,那是另一回事,但我的印象是这是不可接受的。

@Ismael-VC,我可以看到允许(x)ᵀ作为上标的后缀函数语法——因为它还有什么意思? 我认为您的建议开始以错误的方式惹恼人们的地方是允许将任何标识符作为后缀语法中的函数应用。 我会将其限制为上标。

@StefanKarpinski ,我认为共识正是允许.'表示非递归、非共轭数组转置(如果我们有这个运算符),而'是递归、共轭伴随操作。

我真的非常讨厌使用作为后缀转置运算符的想法。 在变量名中作为上标太有用了,比如aᵀaLᵀ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)ᵀ应该是替代(完全等效)的有效语法。

来自https://github.com/JuliaLang/julia/issues/20978#issuecomment -315902532 的方法(1),得到了很好的支持(例如 https://github.com/JuliaLang/julia/issues/20978# issuecomment-316080448),仍然有可能。 我有一个分支实现了我可以发布的这种方法(引入flip(A) )。

对于它的价值,我支持弃用.' 。 该线程中的混乱和模棱两可本身就是这样做的有力论据。 最好的!

我相信只要我们有后缀' ,人们就会想用它在 f.(v, w') 向量的笛卡尔积上广播f f.(v, w') 。 人们会想用它来将字符串向量重塑为标题的行向量,以获得类似表格的结构。 因此,对我来说,有一个简单易用的替代品很有吸引力,我们可以指导他们使用。

这是一个我们没有考虑过的选项: A*' — 一个新的二合图。 典型的数学符号可能会将其解释为conj(A)' ,这实际上非常接近我们想要的。 它在 0.6 上已经可用,但在 0.7 上,我们允许使用*来连接字符……不过仍然可以使用。

我不相信后缀"`可用,因为自定义字符串文字解析超出了行尾。 由于同样的原因,后缀*本身也不可用。 后缀素数A′可能是最常用的 unicode 标识符之一,因此它比Aᵀ还要多。

老实说,在查看我的代码之后,我根本不使用.' ,所以transpose(a)可能没问题。

请注意,我刚刚对已注册的包进行了 grep,发现 .' 有 600 多种用法,因此这种情况并不少见。

有没有检查过这个地方,看看是否在.' '本来可以的地方没有使用 .'? 我开始认为这可能是真的。 否则,我看到合法使用.'的唯一地方是在 Plots.jl 标签允许向量(而不是需要字符串的行向量)之前,但这已经改变了。 对于我经常需要这个的代码,我想我会开始在本地做T = transpose ,或者抛出一个宏来将'更改为transpose

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

对于这种罕见的情况,我会很好。

人们会想用它来广播 f 通过向量与 f.(v, w') 的笛卡尔积。 人们会想用它来将字符串向量重塑为标题的行向量,以获得类似表格的结构。 因此,对我来说,有一个简单易用的替代品很有吸引力,我们可以指导他们使用。

如果它只在语句中出现一次,是否可以只使用transpose

conjugate-adjoint 的a*'语法非常好,尽管它看起来并不是我们需要更好语法的操作。 &a将很快推出并建议交换东西,尽管它与传统的符号完全不同。

也许是时候进行一次民意调查了?

我们应该如何拼写结构转置?

(大致按建议顺序;此处不对表情符号名称进行判断)

  • 👍: A.' — 只是改变意思,保持语法不变
  • 👎: transpose(A) — 没有特殊语法
  • 😄: t(A)tr(A) — 没有特殊语法,但导出更短的名称
  • 🎉: Aᵀ — 只有和可能一两个特殊的上标从标识符
  • 😕: (A)ᵀ — 所有上标与标识符分开,表现得像后缀运算符
  • ❤️: A*' — 在那个诡异的山谷上闪闪发光,这意味着结构转置
  • 如果您更喜欢&A ,请在上面的 Stefan 帖子上投一个🎉(我们没有表情符号)

LinAlg 的讨论确实谈到了将.’给非递归转置使用,因为conj(x’)相对不常见。 但是是数学语法,并且确实应该具有数学含义(如果有的话)。

非常强烈反对使tr(A)表示矩阵转置 - 每个人都会认为这意味着矩阵跟踪: https ://en.wikipedia.org/wiki/Trace_ (linear_algebra)

如果不弃用上标作为标识符(在 1.0 之前可能必须认真考虑),那么ᵀ(A)也是可能的。

关于建议(A)ᵀ ,我很抱歉用以下评论稍微偏离了这个讨论:

我从来没有非常关心将用作一元运算符,特别是因为无论如何你最终都会键入√(...) ,只要你想将它应用于一个超过一个或几个字符。 此外,我一直发现√a之间的功能差异非常人为。 如果您了解 Unicode 类等,这可能是有道理的,但对其他人来说,这一定是荒谬的。 当然,将作为有效的变量名很有用,但同样,如果您需要多次使用√a ,它可能是一个有用的变量名来存储a的平方根次。 或更复杂的表达式,如a²b及其平方根a√b ,其中前者是有效标识符,后者不是。 最重要的是,我喜欢一致性。

因此,为了保持一致性,我喜欢在使用括号(A)ᵀ(a)²以及删除 Unicode 一元运算符 (及其亲属)时使用后缀运算符的建议,以便它也可以在标识符中使用(虽然仍然可以作为普通函数调用√(a)访问)。

我 100% 同意@Jutho所说的,并且在很多场合都这么认为。 你介意打开一个问题吗,@Jutho? 建议:允许在标识符名称中使用 ,要求√(x)作为操作调用。

下一个问题-> 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ᵗ

就我个人而言,我对在 Julia v.0.6 中工作的A'非常满意,在它被伴随之前。 伴随使用的频率很高吗?

这是我在表格中的评论:

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 中工作,在它被伴随之前。 伴随使用的频率很高吗?

我不明白, A'一直是A的伴奏。 我们曾经为共轭转置调用底层函数ctranspose ,但我们将其重命名为等效术语adjoint ,而功能没有变化。

如果你在做线性代数,那么无论如何你更有可能想要一个共轭转置,所以你将输入A'而不是transpose(A) 。 不为非共轭转置定义特殊语法的受欢迎程度(可能)部分是由于大多数线性代数用途实际上并不常见需要非共轭转置。

如果你在做线性代数,那么......

如果您的工具是锤子,那么... :)

...您必须考虑 Julia 发展为通用编程语言的可能性。

也许不会,也许它会一直是线性代数 argot - 这是必须考虑像我这样的程序员的可能性。 :)

@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)

将在 Julia 中按字面意思写为:

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

或者假设向量是该 NumPy 示例中的行,并且是 Julia 中的列:

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

这似乎与它得到的一样清晰和数学。 如果您的数据是真实的,那么伴随和转置是相同的操作,这可能就是您在上面使用转置的原因 - 但在数学上伴随是正确的操作。 正如@ararslan所说, X' $ 在 Julia 中一直意味着adjoint (在 Matlab 中也是如此)。 它以前被称为ctranspose是“共轭转置”的缩写,但这个名字用词不当,因为运算符的定义属性是

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

这是Hermitian 伴随的定义属性,但是当A是一个复矩阵时,它恰好被共轭转置满足。 这就是为什么“伴随”是这个运算符的正确通用术语。

综上所述,我在上面对transpose(a)a.'都投了赞成票,因为我认为a.'可以表示结构转置。 它会按预期工作,即使它不是递归的,因此在某些通用代码中不是“数学上正确的”,让它按预期工作似乎已经足够好了。 告诉人们考虑在通用代码中使用conj(a')似乎是一种教育,而不是我们真正需要用它来打击人们的东西。

@mahiki如果由于某种原因您确实需要在代码中多次使用transpose而不是adjoint ,那么您可以定义一个较短的宏,例如@t别名transpose (虽然我知道这个解决方案并不理想,尤其是当你和其他人一起编写代码时)。

您必须考虑 Julia 发展为通用编程语言的可能性。

@Liso77已经是了。 作为众多示例之一,Nanosoldier 运行一个 Web 服务器,该服务器侦听 GitHub 事件并按需运行性能基准测试,所有这些都在 Julia 中。 不过这是题外话,我不希望这个线程脱离主题。

如果你用非数字数据转置某种矩阵——这是一个完全有效的用例——数学转置表示法实际上似乎是一个糟糕的双关语。 在这种情况下,我觉得最好更明确地说明您的要求,例如transpose (甚至permutedims ,具体取决于您的具体需求)。

如果你用非数字数据转置某种矩阵——这是一个完全有效的用例——数学转置表示法实际上似乎是一个糟糕的双关语。

由于A.'在通常意义上并不是真正的“数学转置表示法”,因此我认为这不是支持或反对的论据。

我认为@ararslan并不是反对现有的.'而是反对引入上标-T 语法。 我倾向于同意 - 如果您的意思是伴随的线性代数概念,那么您应该使用' (即使您的矩阵恰好是真实的)。 如果你有一个非数字数据的矩阵,那么置换两个索引当然是完全合法的,但是这个操作并不是我们通常认为的“转置”,并且使用数学上标-T 表示法是可能更容易混淆而不是澄清。 上标-T 表示法真正适合的唯一情况是,如果您有一个数字矩阵,其索引要置换,但您真的想要伴随线性运算符。 这种情况当然存在,但可能太少见而不足以证明引入新语法的合理性。

...但是这个操作并不是我们通常认为的“转置”,...

如果这很不寻常,为什么 ararslan 和许多其他人投票支持将结构转置拼写为transpose(A)

谢谢@StefanKarpinski @ararslan @ttparker。 我不得不回到我的线性代数课本并重新发现伴随词,它就在那里。 我在进行复杂分析之前就采用了它,这可能是我没有注意到它的原因。

我喜欢能够做到这一点
gradient = (1/m) * (X'Θ - Y) * X'

我的困惑源于在参考文档、论文、教科书等中广泛使用“转置”(作为上标 T),例如Andrew Ng 的 stanford CS229 讲座笔记,其中相应的 Julia 代码将使用adjoint作为在上面@StefanKarpinski的干净示例中。 这是因为伴随和转置在ℝ (对吗?)中是等价的。 更新:是的

现在,我最喜欢的转置表示法就是逻辑上一致的任何东西。 显然.'不是因为与点分运算符语法冲突,我不反对transpose(A)没有特殊语法,因为除了 unicode 上标之外,似乎没有合理的特殊语法可用。

如果我发现自己写了很多转置宏@t别名transpose ,我喜欢@ttparker解决方案。

再次,我错误地陈述:

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)语法?)? 如果不是,那么这就是一个问题,因为它会破坏https://github.com/JuliaLang引入的 if-you-can-define-the-symbol-name-then-you-can-override-its-syntax 规则

编辑:看起来可用:

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

关键是fDf必须使用transpose定义,并且不能使用伴随词。

我不认为复杂的步骤方法在朱莉娅中是超级相关的。 在语言支持有效的内置复数但无法定义等效有效的Dual数字类型的情况下,获得自动微分不是一个巧妙的技巧/解决方法吗? Julia 中的情况并非如此,它有非常好的自动微分库。

我同意使用对偶数而不是复数步法,这是你提出的一个很好的观点(我个人已经在 julia 中用双数法替换了我所有的复数步法评估)。 但是,我确实认为这仍然是一个有效的用例,用于演示目的、教学技巧(例如,参见Nick Higham 在 Julia Con 2018 上谈论复杂步骤方法)和可移植性(换句话说,我担心上面使用复数的 MATLAB 版本的代码会更简洁)。

来自工程师和可能的物理学家的世界,他们使用复杂数组而不是真实数组,没有转置运算符有点痛苦。 (谐波时间依赖性的复杂相量表示在我们的领域中无处不在。)我个人更喜欢 xH 和 xT 的 numpy 语法,尽管我唯一考虑的是简洁性。

在我的代码中,转置运算符相对于 Hermitian 转置的密度约为 1 比 1。 所以非共轭转置对我来说同样重要。 转置的很多用途是创建外部产品并正确调整数组大小,以便与其他代码接口或矩阵乘法。

我现在打算简单地为操作提供一个宏或一个字符函数,但是与旧功能 transpose() 或 permutedims() 的正确等价物是什么?

transpose用于线性代数并且是递归的,而permutedims用于任何类型的数据的非递归排列。

有趣的是,您说您使用转置和伴随使用一样多。 我以前也是这样,但主要是因为我倾向于在我的数据是真实的地方犯错误,所以我倾向于转置,但实际上伴随是正确的操作(推广到复杂的情况 - 伴随是我算法的正确操作)。 当然,有(许多)有效的例外。

在与电动力学相关的所有内容中,您经常使用类空间向量并希望在 R^n(通常 n=3)中使用向量运算,即特别是transpose ,即使您的向量是复值的,因为您已经进行了傅里叶变换。 @mattcbro似乎在谈论这种应用程序。

话虽如此,在阅读语法讨论时,我经常考虑就我个人而言,我无法想象稍微冗长的语法会降低我的编程速度或效率。 考虑算法本身以及最自然/最有效的实现方式需要更多时间。

在与电动力学相关的所有内容中,您经常使用类似空间的向量并希望在 R^n(通常 n = 3)中使用向量运算,即特别是转置,即使您的向量是复值,因为您采用了傅立叶转变。

不必要。 通常,您需要傅立叶幅度的时间平均量,在这种情况下,您使用复点积,例如 ½ℜ[𝐄*×𝐇] 是复傅立叶分量的时间平均坡印廷通量,而 ¼ε₀|𝐄|² 是时间平均真空能量密度。 另一方面,由于麦克斯韦算子(通常)是复对称算子(“倒数”),因此您经常在域𝐄(𝐱)等上使用非共轭“内积”来进行(无限维)代数等。所有空间。

没错,我经常在第一句话中使用这个词,但显然删除了它:-)。

好吧,如果你想去那里,电磁量甚至更简洁地写在克利福德代数公式中,通常称为几何代数。 这些代数具有多个自同构和反自同构,它们在理论的形成中起着至关重要的作用,尤其是在考虑散射问题时。

这些代数通常具有简洁的矩阵表示,并且这些态射通常很容易通过复转置、厄米转置和共轭来计算。

尽管如此,正如我之前所说,我对转置的主要用途通常是安排我的数组以与其他数组、其他代码接口,并让矩阵乘法与展平数组的正确维度一起工作。

我个人更喜欢 xH 和 xT 的 numpy 语法

现在在 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定义为用户代码的好主意。 从长远来看,这很可能会破坏事情;-) 虽然也许有一天我们会对在Base x.T的后果有足够好的感觉。

但在一般意义上,我确实想知道为什么在泛型接口中使用这种属性来定义“getter”实际上是不好的。 例如,通用字段 getter 函数目前有一个巨大的命名空间问题,只需明智地使用getproperty即可解决。 写x.A比写MyModule.A(x) A get_my_A(x)模块。 在我看来,唯一的问题是预期能够为子类型覆盖.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 等级