Julia 如何计算矩阵作为向量保存的矩阵乘法

Julia 如何计算矩阵作为向量保存的矩阵乘法,julia,Julia,我有两个对称矩阵A,B和一个向量X。A的维度是n乘n,B的维度是n乘n,X的维度是n乘1。让矩阵A的ith行和jth列的元素用A[i,j]表示 由于A是对称的,因此只保存A的上三角矩阵的每一列。矩阵A保存为数组: Vector_A = [A[1,1], A[1,2], A[2,2], A[1,3], A[2,3], A[3,3], A[1,4], A[2,4], A[3,4], A[4,4], ...

我有两个对称矩阵
A
B
和一个向量
X
A
的维度是n乘n,
B
的维度是n乘n,
X
的维度是n乘1。让矩阵
A
i
th行和
j
th列的元素用
A[i,j]
表示

由于
A
是对称的,因此只保存
A
的上三角矩阵的每一列。矩阵
A
保存为数组:

Vector_A = [A[1,1],
            A[1,2], A[2,2],
            A[1,3], A[2,3], A[3,3],
            A[1,4], A[2,4], A[3,4], A[4,4],
            ...,
            A[1,n], A[2,n], ..., A[n,n]]
矩阵
B
以与矩阵
A
相同的格式保存。现在我想计算
ABA
而不将
向量A
向量B
转换回矩阵
A,B
。由于
ABA
也是对称的,因此我希望以与数组相同的方式保存
ABA
。我怎样才能在朱莉娅身上做到


我还想计算
X'AX
而不将
向量A
转换回矩阵
A
,其中
X'
表示
转置(X)
。如何在Julia中实现它?

您需要实现自己的数据结构,这些数据结构继承自
AbstractMatrix
类型

例如,这可以通过以下方式实现:

struct SymmetricM{T} <: AbstractMatrix{T}
    data::Vector{T}
end
在此代码中,每次对矩阵执行检查边界时,将计算nr。也许在您的生产实现中,您可能希望将其移动为
SymmetricM
的一个字段。你可以增加一些弹性,多存储8个字节,但速度会有所提高

现在我们需要的下一个函数是根据矩阵指数计算向量的位置。下面是一个可能的实现

function getix(idx)::Int
    n = size(m)[1]
    row, col = idx
    #assume left/lower triangular
    if col > row
        row = col
        col = idx[1]
    end
    (row-1)*row/2 + col
end
现在我们可以实现
getindex
setindex
功能:

@inline function Base.getindex(m::SymmetricM, idx::Vararg{Int,2})
    @boundscheck checkbounds(m, idx...)
    m.data[getix(idx)]
end

@inline function Base.getindex(m::SymmetricM{T}, v::T, idx::Vararg{Int,2}) where T
    @boundscheck checkbounds(m, idx...)
    m.data[getix(idx)] = v
end
现在让我们测试一下这个东西:

julia> m = SymmetricM(collect(1:10))
4×4 SymmetricM{Int64}:
 1  2  4   7
 2  3  5   8
 4  5  6   9
 7  8  9  10
您可以看到,我们只提供了一个三角形的元素(无论是下三角形还是上三角形,它们都是相同的),我们得到了完整的矩阵

这确实是一个完全有效的Julia矩阵,因此所有矩阵代数都应该使用它:

julia> m * SymmetricM(collect(10:10:100))
4×4 Array{Int64,2}:
  700   840  1010  1290
  840  1020  1250  1630
 1010  1250  1580  2120
 1290  1630  2120  2940
请注意,乘法的结果是一个矩阵,而不是
SymmetricM
——要获得
SymmetricM
,需要重载
*
运算符以接受2个
SymmetricM
参数。为了便于说明,让我们展示一个带有减号的自定义运算符重载
-

import Base.-
-(m1::SymmetricM, m2::SymmetricM) = SymmetricM(m1.data .- m2.data)
现在您将看到,对
SymmetricM
的减法将返回另一个
SymmetricM

julia> m-m
4×4 SymmetricM{Int64}:
 0  0  0  0
 0  0  0  0
 0  0  0  0
 0  0  0  0
这样你就可以在Julia中建立一个完整的三角矩阵代数系统


但是请注意,
getix
函数有一个
if
语句,因此不使用
data
字段访问
SymmetricM
元素的速度要比常规矩阵慢得多,因此您可能应该尝试重载项目所需的尽可能多的运算符。

是否确实需要有效地存储矩阵?整个阵列的常规内存布局可能会超过性能优势。但是如果你真的需要节省内存,你最好写一个新的
高效对称{T}AFAIK,Julia有稀疏矩阵类型。因此,与其建立自己的优化布局,也许你所需要做的只是使用稀疏矩阵。我上次考虑这一点是在我的软件包中处理算法模型置信集时。我通过两种方式实现了算法:1)只存储矩阵的上三角部分以节省内存;2)存储整个矩阵,这会占用两倍的内存,但允许我使用BLAS例程。第二种算法运行速度明显更快。第一种选择可能确实更适合您的问题,但至少值得研究第二种选择。
julia> m-m
4×4 SymmetricM{Int64}:
 0  0  0  0
 0  0  0  0
 0  0  0  0
 0  0  0  0