LDLt factorization using Scipy's python bindings to LAPACK

时间:2017-11-13 06:11:21

标签: scipy linear lapack

I am trying to get the LDLt factorization of a given symmetric matrix with scipy's python bindings to LAPACK using the dsysv routine which actually solves linear systems using this matrix factorization.

I have tried the following:

import numpy as np
from scipy.linalg.lapack import dsysv

A = np.random.randint(1, 1000, size=(5, 5))
A = (A + A.T)
b = np.random.randn(5)
lult, piv, x, _ = dsysv(A, b, lower=1)

where x would be the solution for the above linear system and lult and piv contain information about the factorization.

How can I reconstruct LDLt from it? Sometimes negative values are contained in piv and from the docs I was not able to understand their meaning.

LAPACK's sytrf actually computes this factorization (without solving any linear system) but it does not seem available via Scipy.

Update: There is an example here with the output I am interested in (see eq. 3-23).

3 个答案:

答案 0 :(得分:2)

所有必需的信息都在documentation of systrf中找到。但诚然,这有点冗长。

所以请给我代码:

import numpy as np
from scipy.linalg.lapack import dsysv

def swapped(i, k, n):
    """identity matrix where ith row and column are swappend with kth row and column"""
    P = np.eye(n)
    P[i, i] = 0
    P[k, k] = 0
    P[i, k] = 1
    P[k, i] = 1
    return P


# example

n = 5

A = np.random.rand(n, n)
A = (A + A.T)
b = np.random.randn(n)
lult, piv, x, _ = dsysv(A, b, lower=1)


# reconstruct L and D

D = np.zeros_like(A, dtype=float)
L = np.eye(n)

k = 0
while k < n:
    i = piv[k]

    if i < 0:
        s = 2
    else:
        s = 1

    if s == 1:
        i = i - 1
        D[k, k] = lult[k, k]  # D(k) overwrites A(k,k)
        Pk = swapped(k, i, n)
        v = lult[k+1:n, k]  # v overwrites A(k+1:n,k)
        Lk = np.eye(n)
        Lk[k+1:n, k] = v
    else:
        m = -i - 1
        D[k:k+2, k:k+2] = lult[k:k+2, k:k+2]  #  the lower triangle of D(k) overwrites A(k,k), A(k+1,k), and A(k+1,k+1)
        D[k, k+1] = D[k+1, k]  # D is symmeric
        Pk = swapped(k+1, m, n)
        v = lult[k+2:n, k:k+2]  # v overwrites A(k+2:n,k:k+1)
        Lk = np.eye(n)
        Lk[k+2:n, k:k+2] = v   

    L = L.dot(Pk).dot(Lk)

    if s == 1:
        k += 1
    else:
        k += 2

print(np.max(np.abs(A - L.dot(D).dot(L.T))))  # should be close to 0

上面的剪切从分解中重建L和D(它需要适应从UDUt分解重建U)。我将尝试在下面解释。首先是文档中的引用:

  

...需要额外的行交换才能明确恢复U或L(很少需要)。

重建L(或U)需要多次迭代,包括行交换操作和矩阵乘法。这不是非常有效(在Python中完成时不那么有效)但幸运的是,这种重建很少是必要的。所以要确保你真的必须这样做!

我们从L重建L = P(1)*L(1)* ... *P(k)*L(k)*...,。 (Fortran指数基于1)。所以我们需要将k从0迭代到n,在每一步中获得K和L并乘以它们。

P是一个由piv定义的置换矩阵。 piv的正值是直接的(i = piv[k])。这意味着在执行操作之前,在A中交换了第i行和第k行/列。在这种情况下,lult的第k个对角元素对应于D的第k个对角元素。 L(k)包含较低对角矩阵的第k列 - 交换后。

负值piv表示D的对应元素是2x2块而不是一个元素,L(k)对应于下对角矩阵的两列。 / p>

现在,对于k中的每个步骤,我们获取L(k),应用交换操作P(k),并将其与现有L合并。我们还获得了D的1x1或2x2块,并相应地将k增加1或2,用于下一步。

我不会因为不理解我的解释而责怪任何人。我只是把它写下来,因为我想出来了......希望代码片段,描述和原始文档的组合证明是有用的:)

答案 1 :(得分:1)

dsysv是线性系统求解器,它内部完成所有魔法,包括对dsytrf的调用。因此,对于分解,不需要它。正如kazemakase所提到的,现在可以在SciPy中使用(PR 7941并且将在1.1版中正式出现),您可以使用scipy.linalg.ldl()来获取外部因素的分解和置换信息。实际上,这就是添加?sytrf?hetrf的原因。

您可以查看其源代码,了解ipiv的清理方式。

在Windows 10机器上使用OpenBlas构建的SciPy v.1.1与使用mkl的matlab构建,性能如下所示

enter image description here

在它之上添加额外的JIT编译器可能会使它达到matlab的速度。由于ipiv处理和分解构造是在纯numpy / python中完成的。如果性能至关重要,或者更好地进行cython化。

答案 2 :(得分:0)

将scipy更新为版本&gt; = 1.0.0应该可以解决问题。

sytrf的包装器已于9月中旬在1.0.0 Beta release之前添加到主分支中。 您可以在Github上找到相关的pull-requestcommit