julia - 如何找到一个复矩阵 A 使得 A * Transpose(A) = B,其中 B = Transpose(B) 并且是复数?

标签 julia linear-algebra

我想找到一个复值矩阵 A,它满足属性 A * Transpose(A) = B。矩阵 B 是复数且对称的(不是 Hermitian 矩阵),即 B = Transpose(B)

我在 Julia 工作。我正在尝试以下优化代码,但它不起作用(而且速度也非常慢):

using LinearAlgebra
using Optim


# Define the objective function for optimization
function objective(A)
    return sum(abs2.(A * transpose(A) - B))  # Sum of squared element wise differences
end


# Initialize a random initial guess for A
n = size(B, 1)
initial_guess = rand(ComplexF64, n, n)


# Perform optimization
result = optimize(objective, initial_guess, BFGS(), Optim.Options(show_trace=true))


# Get the optimized matrix_B_eta from the result
matrix_A_optimized = reshape(result.minimizer, n, n)

如何构造矩阵A?

最佳答案

这里的编辑包括一个不同的、更好的(即它应该更快、更稳健)基于 Bunch-Kaufman 矩阵分解的方法。我已将原始帖子留在下面,因此评论仍然有意义。

由于 StackOverflow 由于某些愚蠢的原因不支持 MathJax,因此写下正在发生的数学运算有点痛苦,但本质是

Factor B via Bunch-Kaufman: B=Transpose(P)*U*D*Transpose(U)*P
where P is a permutation matrix
      U is an upper triangular matrix
      D is a block diagonal matrix. The blocks are all 1x1 or 2x2
Then A=Transpose(P)*U*Sqrt(D) we have B=A*Transpose(A)
Note Transpose in the above really is transpose, no complex conjugation is going on.

这是代码。我再说一次,我是一个 Julia 新手,所以任何改进它的方法都将非常感激 - 特别是 Sqrt(D ) 函数不是很优雅,但它可以工作,并且不会对时间至关重要我'我不太担心。也就是说,在矩阵 sqrt 函数中进行编译似乎确实花费了过多的时间,特别是因为它仅用于 2x2 矩阵:

using LinearAlgebra

function SymmetricFactorComplexSymmetricMatrix( B::Symmetric{ T } ) where { T <: Complex }

    # Main driver

    # Given a complex symmetric matrix B return A such that A * A(T)=B

    # Method used is via Bunch-Kaufman factorisation 

    # Factor B such that B=P'UDU'P where
    # P is a permutation matrix
    # U is a upper triangular matrix
    # D is a block diagonal matrix, the blocks being either 1x1 or 2x2
    # Due to B being symmetric, D is also symmetric
    D, U, p = bunchkaufman( B )

    # Form A = P'U*Sqrt(D)
    D = SymTridiagonal( D )
    A = PermuteResult( U * CalcSqrt2x2BlockDiagonalD( D ), p )

    return A
    
end

function CalcSqrt2x2BlockDiagonalD( D::SymTridiagonal{ T } )  where { T <: Complex }

    # Find the square root of the D matrix reurned by the Bunch-Kaufman method
    
    dv = Vector{ T }( undef, size( D, 1 )     )
    ev = Vector{ T }( undef, size( D, 1 ) - 1 )
    
    i = 1
    while i < size( D, 1 )
        # Work out if this is a 2x2 or 1x1 block
        D2x2Block = @view D[ i:i + 1, i: i + 1 ]
        if abs( D2x2Block[ 1, 2 ] ) ≉ 0.0
            # A 2x2 block. Take the sqrt and poke it into the
            # arrays that will form the result
            SqrtBlock = sqrt( D2x2Block )
            dv[ i     ] = SqrtBlock[ 1, 1 ]
            ev[ i     ] = SqrtBlock[ 2, 1 ]
            dv[ i + 1 ] = SqrtBlock[ 2, 2 ]
            if i ≠ size( D, 1 ) - 1
                ev[ i + 1 ] = 0.0
            end
            i = i + 2
        else
            # A 1x1 block. Take the sqrt and poke it into the
            # arrays that will form the result
            dv[ i ] = sqrt( D[ i, i ] )
            ev[ i ] = 0.0
            i = i + 1
        end
    end
    # If required catch the last element. Must be a 1x1 block
    if i ==  size( D, 1 )
        dv[ i ] = sqrt( D[ i, i ] )
    end
    
    return SymTridiagonal( dv, ev )
    
end

function PermuteResult( Fin::Matrix{ T }, p::Vector{ IT } ) where { T <: Complex, IT <: Integer }

    # Permute the Fin matrix accordin to the permutation given by p

    Fout = similar( Fin )
    
    for i in axes( Fin, 2 )
        Fout[ p, i ] = Fin[ :, i ] 
    end

    return Fout

end

# Checking functions

function CheckIt( A::Symmetric{ T } ) where{ T <: Complex }

    F = SymmetricFactorComplexSymmetricMatrix( A )

    return maximum( abs.( A - F * ComplexTranspose( F ) ) )

end

function ComplexTranspose( Q::Matrix )
    QT = similar( Q )
    for j in axes( Q, 1 )
        for i in axes( Q, 2 )
            QT[ i, j ] = Q[ j, i ]
        end
    end
    return QT

end

function ManyTest( ntests::Integer, maxsize = 1000::Integer )

    maxerror = 0.0
    for i = 1:ntests
        n = Int( floor( maxsize * rand() + 1 ) )
        A = Symmetric( rand( ComplexF64, n, n ) )
        error = CheckIt( A )
        println( "Test ", i, " size ", n, " error ", error )
        maxerror = max( error, maxerror )
    end
    printstyled( "Max error: ", maxerror, "\n"; color = :lightred )

    return maxerror

end

这是正在测试的

ijb@LAPTOP-GUG8KQ9I:~/julia/test$ julia -O3
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.9.0 (2023-05-07)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> using Revise, OhMyREPL, Debugger, Test, Pkg

julia> include( "CompSym2.jl")
ManyTest (generic function with 2 methods)

julia> ManyTest(25,5000)
Test 1 size 3411 error 8.1594242764668e-13
Test 2 size 1484 error 5.172734121734416e-13
Test 3 size 4184 error 8.575452117894415e-13
Test 4 size 2018 error 4.830629499161676e-13
Test 5 size 1658 error 6.091354345513839e-13
Test 6 size 2841 error 1.1587510250913577e-12
Test 7 size 1589 error 7.092154735016015e-13
Test 8 size 4276 error 1.2948552601956827e-12
Test 9 size 3435 error 1.0944800551535788e-12
Test 10 size 1075 error 5.827720133625812e-13
Test 11 size 3237 error 1.1542970580509046e-12
Test 12 size 861 error 1.9065513187377532e-13
Test 13 size 946 error 2.5678632958931523e-13
Test 14 size 3953 error 1.2280159009015767e-12
Test 15 size 4737 error 8.789983456404833e-13
Test 16 size 4477 error 2.315395050493212e-12
Test 17 size 3024 error 5.390843920673155e-13
Test 18 size 3610 error 1.9573189511434918e-12
Test 19 size 273 error 9.825630577843e-14
Test 20 size 1170 error 6.206737441326594e-13
Test 21 size 1511 error 5.482164493856334e-13
Test 22 size 770 error 2.495593681148973e-13
Test 23 size 738 error 2.9098725209054565e-13
Test 24 size 2692 error 1.059692174415002e-12
Test 25 size 2288 error 5.528934962642065e-13
Max error: 2.315395050493212e-12
2.315395050493212e-12

原始方法:

这是一个似乎可行的想法,但请注意下面的警告。另请注意,我是 Julia 新手(但在 Fortran 和 C 方面非常有经验),所以这可能是能够用任何语言编写 Fortran 的一个很好的例子......

function ComplexTranspose( Q::Matrix )
    QT = similar( Q )
    for j in axes( Q, 1 )
        for i in axes( Q, 2 )
            QT[ i, j ] = Q[ j, i ]
        end
    end
    return QT
end

function GenQOrtho( Q::Matrix )
    QOrtho = copy( Q )
    for i in axes( QOrtho, 2 )
        α = sum( QOrtho[ :, i ] .* QOrtho[ :, i ] )
        α = sqrt( α )
        QOrtho[ :, i ] = QOrtho[ :, i ] .* ( 1.0 / α )
    end
    return QOrtho
end

function SymmetricFactorComplexSymmetricMatrix( A::Matrix{ T } ) where { T <: Complex }

    λ, Q = eigen( A )

    QOrtho = GenQOrtho( Q )

    return Diagonal( sqrt.( λ ) ) * ComplexTranspose( QOrtho )
    
end

function CheckIt( A::Matrix )

    F = SymmetricFactorComplexSymmetricMatrix( A )

    return maximum( abs.( A - ComplexTranspose( F ) * F ) )

end

function ManyTest( ntests::Integer )

    maxerror = 0.0
    for i = 1:ntests
        n = Int( floor( 100 * rand() + 1 ) )
        A = Matrix( Symmetric( rand( ComplexF64, n, n ) ) )
        error = CheckIt( A )
        println( "Test ", i, " size ", n, " error ", error )
        maxerror = max( error, maxerror )
    end
    printstyled( "Max error: ", maxerror, "\n"; color = :lightred )

    return maxerror

结束

在 REPL 中运行:

ijb@LAPTOP-GUG8KQ9I:~/julia/test$ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.9.0 (2023-05-07)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> using Revise, OhMyREPL, Debugger, Test, Pkg, LinearAlgebra

julia> include( "CompSym.jl")
ManyTest (generic function with 1 method)

julia> ManyTest(25)
Test 1 size 83 error 9.944536531374647e-14
Test 2 size 11 error 4.491946442396235e-15
Test 3 size 4 error 8.95090418262362e-16
Test 4 size 48 error 5.541685356342546e-14
Test 5 size 58 error 6.776845358219464e-14
Test 6 size 83 error 1.2052768509123334e-13
Test 7 size 23 error 1.602186845968893e-14
Test 8 size 79 error 1.1038213408936726e-13
Test 9 size 77 error 6.335118026431236e-13
Test 10 size 27 error 1.5122688478052736e-14
Test 11 size 62 error 1.3015134857799234e-13
Test 12 size 59 error 9.97847338085134e-14
Test 13 size 36 error 3.466048004558062e-14
Test 14 size 28 error 1.9942028578972164e-14
Test 15 size 74 error 1.899255854395612e-13
Test 16 size 13 error 4.228354934573259e-15
Test 17 size 83 error 3.63646988782893e-13
Test 18 size 56 error 6.795807448681065e-14
Test 19 size 89 error 1.6855954444400822e-13
Test 20 size 97 error 8.38314879211208e-14
Test 21 size 69 error 8.592074666261367e-14
Test 22 size 7 error 3.5283439685664285e-15
Test 23 size 5 error 1.6653345369377348e-15
Test 24 size 60 error 6.41406310718282e-14
Test 25 size 35 error 2.6668010011248128e-14
Max error: 6.335118026431236e-13

它基于复对称矩阵的特征向量是 orthogonal in a transpose sense and not in the usual conjugate transpose sense 的事实。因此,我们可以使用正常的特征值机制,但必须仔细地重新规范化特征向量,以便向量的长度在转置中为 1,而不是伴随意义 - 这有点痛苦,因为大多数 Julia 工具,例如dot()norm() 假设您想要共轭(就像您通常做的那样),所以这里您必须“手动”做一些工作。我怀疑这就是您的解决方案不起作用的原因 - transpose 函数正在执行共轭,因为它看到一个复杂的矩阵。

需要注意的是,我并不 100% 相信当你有退化时这总是有效。问题是 Julia 似乎没有专门的复杂对称对角线器,所以我不明白为什么对应于简并 eval 的 evec 保证是正交的(在任何意义上)。我可以看到如何解决这个问题(在子空间中对角化),但目前我无法生成这样的示例,而且我太累了,无论如何都无法编写代码。我周末看看是否可以。

关于julia - 如何找到一个复矩阵 A 使得 A * Transpose(A) = B,其中 B = Transpose(B) 并且是复数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76884871/

相关文章:

julia - 从 Julia 中的离散分布中抽样

numpy - 使用 numpy 求解齐次系统 Ax=0

java - 最小二乘法 3D

c - 使用 Lapack&co 求解病态线性方程组

c++ - 用 Eigen 求解小型齐次线性系统的最快方法

linker - 链接器是否更喜欢 .so 文件而不是 .a 文件?

plot - 如何在一个 Gadfly 图中重叠绘制多个数据数组?

Julia - 如何知道最新版本 (v1.0.0) 等效功能是什么?

recursion - 重新分配函数并避免在 Julia 中进行递归定义

c++ - Eigen 稀疏矩阵行列式为零