插值矩阵分解 (scipy.linalg.interpolative)#

Added in version 0.13.

矩阵 \(A \in \mathbb{C}^{m \times n}\) 的插值分解(ID),其秩为 \(k \leq \min \{ m, n \}\),是一种因式分解

\[ \begin{align}\begin{aligned}A \Pi = \begin{bmatrix} A \Pi_{1} & A \Pi_{2} \end{bmatrix} = A \Pi_{1} \begin{bmatrix} I & T \end{bmatrix},\\A \Pi = \begin{bmatrix} A \Pi_{1} & A \Pi_{2} \end{bmatrix} = A \Pi_{1} \begin{bmatrix} I & T \end{bmatrix},\end{aligned}\end{align} \]

其中 \(\Pi = [\Pi_{1}, \Pi_{2}]\) 是一个置换矩阵,且 \(\Pi_{1} \in \{ 0, 1 \}^{n \times k}\),即 \(A \Pi_{2} = A \Pi_{1} T\)。这可以等价地写成 \(A = BP\),其中 \(B = A \Pi_{1}\)\(P = [I, T] \Pi^{\mathsf{T}}\) 分别是 骨架矩阵插值矩阵

如果 \(A\) 的秩不精确为 \(k\),则存在一种形式的近似,即ID,使得 \(A = BP + E\),其中 \(\| E \| \sim \sigma_{k + 1}\) 的数量级为 \(A\) 的第 \((k + 1)\) 大的奇异值。注意,\(\sigma_{k + 1}\) 是秩为 \(k\) 的近似的最优误差,实际上,它由奇异值分解(SVD) \(A \approx U S V^{*}\) 实现,其中 \(U \in \mathbb{C}^{m \times k}\)\(V \in \mathbb{C}^{n \times k}\) 具有正交列,且 \(S = \mathop{\mathrm{diag}} (\sigma_{i}) \in \mathbb{C}^{k \times k}\) 是对角矩阵,其对角元素为非负数。与SVD相比,使用ID的主要优势在于:

  • 建造成本更低;

  • 它保留了 \(A\) 的结构;并且

  • 在考虑 \(P\) 的单位子矩阵的情况下,计算效率更高。

例程#

主要功能:

interp_decomp(A, eps_or_k[, rand])

计算矩阵的ID。

reconstruct_matrix_from_id(B, idx, proj)

从其ID重建矩阵。

reconstruct_interp_matrix(idx, proj)

从ID重建插值矩阵。

reconstruct_skel_matrix(A, k, idx)

从ID重建骨架矩阵。

id_to_svd(B, idx, proj)

将ID转换为SVD。

svd(A, eps_or_k[, rand])

通过ID计算矩阵的SVD。

estimate_spectral_norm(A[, its])

通过随机幂方法估计矩阵的谱范数。

estimate_spectral_norm_diff(A, B[, its])

通过随机幂方法估计两个矩阵差异的谱范数。

estimate_rank(A, eps)

使用随机化方法估计矩阵的秩到指定的相对精度。

支持函数:

seed([seed])

在ID包中使用的内部随机数生成器中播种。

rand(*shape)

通过一种非常高效的滞后斐波那契方法生成标准的均匀伪随机数。

引用

该模块使用了Martinsson、Rokhlin、Shkolnisky和Tygert开发的ID软件包 [R5a82238cdab4-1],这是一个用于通过各种算法计算ID的Fortran库,包括 [R5a82238cdab4-2] 的秩揭示QR方法和 [R5a82238cdab4-3][R5a82238cdab4-4][R5a82238cdab4-5] 中描述的较新的随机化方法。该模块以一种方便Python用户的方式暴露其功能。请注意,该模块除了组织一个更简单和更一致的接口外,没有增加任何功能。

我们建议用户也参考 ID 包的文档

[R5a82238cdab4-1]

P.G. Martinsson, V. Rokhlin, Y. Shkolnisky, M. Tygert. “ID: 一个通过插值分解实现矩阵低秩近似的软件包,版本0.2。” http://tygert.com/id_doc.4.pdf.

[R5a82238cdab4-2]

H. Cheng, Z. Gimbutas, P.G. Martinsson, V. Rokhlin. “On the compression of low rank matrices.” SIAM J. Sci. Comput. 26 (4): 1389–1404, 2005. DOI:10.1137/030602678.

[R5a82238cdab4-3]

E. Liberty, F. Woolfe, P.G. Martinsson, V. Rokhlin, M. Tygert. “Randomized algorithms for the low-rank approximation of matrices.” Proc. Natl. Acad. Sci. U.S.A. 104 (51): 20167–20172, 2007. DOI:10.1073/pnas.0709640104.

[R5a82238cdab4-4]

P.G. Martinsson, V. Rokhlin, M. Tygert. “矩阵分解的随机算法。” 应用计算调和分析 30 (1): 47–68, 2011. DOI:10.1016/j.acha.2010.02.003.

[R5a82238cdab4-5]

F. Woolfe, E. Liberty, V. Rokhlin, M. Tygert. “A fast randomized algorithm for the approximation of matrices.” Appl. Comput. Harmon. Anal. 25 (3): 335–366, 2008. DOI:10.1016/j.acha.2007.12.002.

教程#

初始化#

第一步是通过执行以下命令导入 scipy.linalg.interpolative

>>> import scipy.linalg.interpolative as sli

现在让我们构建一个矩阵。为此,我们考虑一个希尔伯特矩阵,它以低秩著称:

>>> from scipy.linalg import hilbert
>>> n = 1000
>>> A = hilbert(n)

我们也可以通过以下方式明确地执行:

>>> import numpy as np
>>> n = 1000
>>> A = np.empty((n, n), order='F')
>>> for j in range(n):
...     for i in range(n):
...         A[i,j] = 1. / (i + j + 1)

注意在 numpy.empty 中使用 order='F' 标志。这将以Fortran连续顺序实例化矩阵,并在传递到后端时避免数据复制,这一点非常重要。

然后我们通过将其视为 scipy.sparse.linalg.LinearOperator 来定义矩阵的乘法例程:

>>> from scipy.sparse.linalg import aslinearoperator
>>> L = aslinearoperator(A)

这会自动设置描述矩阵及其伴随矩阵对向量作用的方法。

计算一个ID#

我们有几种算法可以选择来计算一个ID。这些算法主要根据两种二分法来划分:

  1. 矩阵的表示方式,即通过其元素或通过其对向量的作用;以及

  2. 是否将其近似为固定的相对精度或固定的秩。

我们在下面依次介绍每个选择。

在所有情况下,ID 由三个参数表示:

  1. 一个秩 k

  2. 一个索引数组 idx;以及

  3. 插值系数 proj

ID 由关系 np.dot(A[:,idx[:k]], proj) == A[:,idx[k:]] 指定。

从矩阵条目#

我们首先考虑一个以其元素表示的矩阵。

要计算一个固定精度的ID,请输入:

>>> eps = 1e-3
>>> k, idx, proj = sli.interp_decomp(A, eps)

其中 eps < 1 是所需的精度。

要计算一个ID到一个固定排名,使用:

>>> idx, proj = sli.interp_decomp(A, k)

其中 k >= 1 是所需的秩。

这两种算法都使用随机采样,通常比相应的旧的、确定性算法更快,这些旧算法可以通过以下命令访问:

>>> k, idx, proj = sli.interp_decomp(A, eps, rand=False)

和:

>>> idx, proj = sli.interp_decomp(A, k, rand=False)

分别地。

从矩阵动作#

现在考虑一个矩阵,它以对向量的作用表示为一个 scipy.sparse.linalg.LinearOperator

要计算一个固定精度的ID,请输入:

>>> k, idx, proj = sli.interp_decomp(L, eps)

要计算一个ID到一个固定排名,使用:

>>> idx, proj = sli.interp_decomp(L, k)

这些算法是随机的。

重建一个ID#

上述ID例程不会显式输出骨架和插值矩阵,而是以更紧凑(有时更有用)的形式返回相关信息。要构建这些矩阵,请编写:

>>> B = sli.reconstruct_skel_matrix(A, k, idx)

对于骨架矩阵和:

>>> P = sli.reconstruct_interp_matrix(idx, proj)

用于插值矩阵。然后可以计算ID近似为:

>>> C = np.dot(B, P)

这也可以直接使用以下方式构建:

>>> C = sli.reconstruct_matrix_from_id(B, idx, proj)

无需首先计算 P

或者,也可以使用以下方式显式地完成:

>>> B = A[:,idx[:k]]
>>> P = np.hstack([np.eye(k), proj])[:,np.argsort(idx)]
>>> C = np.dot(B, P)

计算一个SVD#

可以通过以下命令将ID转换为SVD:

>>> U, S, V = sli.id_to_svd(B, idx, proj)

SVD 近似值为:

>>> approx = U @ np.diag(S) @ V.conj().T

SVD 也可以通过将 ID 和转换步骤合并为一个命令来“全新”计算。根据上述各种 ID 算法,相应地也有多种 SVD 算法可供选择。

从矩阵条目#

我们首先考虑针对以条目形式给出的矩阵的SVD算法。

要计算一个固定精度的SVD,请输入:

>>> U, S, V = sli.svd(A, eps)

要计算一个固定秩的SVD,请使用:

>>> U, S, V = sli.svd(A, k)

两种算法都使用随机抽样;对于确定性版本,如上所述发出关键字 rand=False

从矩阵动作#

现在考虑一个矩阵,它是根据其对向量的作用给出的。

要计算一个固定精度的SVD,请输入:

>>> U, S, V = sli.svd(L, eps)

要计算一个固定秩的SVD,请使用:

>>> U, S, V = sli.svd(L, k)

实用程序例程#

还提供了几个实用程序例程。

要估计矩阵的谱范数,请使用:

>>> snorm = sli.estimate_spectral_norm(A)

该算法基于随机幂方法,因此只需要矩阵-向量乘积。可以通过关键字 its 设置迭代次数(默认:its=20)。矩阵被解释为 scipy.sparse.linalg.LinearOperator,但也可以将其作为 numpy.ndarray 提供,在这种情况下,它使用 scipy.sparse.linalg.aslinearoperator 进行简单转换。

同样的算法也可以估计两个矩阵 A1A2 之差的谱范数,如下所示:

>>> A1, A2 = A**2, A
>>> diff = sli.estimate_spectral_norm_diff(A1, A2)

这对于检查矩阵近似的准确性通常很有用。

scipy.linalg.interpolative 中的一些例程也需要估计矩阵的秩。这可以通过以下任一方式完成:

>>> k = sli.estimate_rank(A, eps)

或:

>>> k = sli.estimate_rank(L, eps)

取决于表示形式。参数 eps 控制数值秩的定义。

最后,所有随机化例程所需的随机数生成可以通过 scipy.linalg.interpolative.seed 进行控制。要重置种子值为其原始值,请使用:

>>> sli.seed('default')

要指定种子值,请使用:

>>> s = 42
>>> sli.seed(s)

其中 s 必须是一个整数或包含 55 个浮点数的数组。如果是一个整数,则通过使用给定整数种子的 numpy.random.rand 来获取浮点数数组。

要简单地生成一些随机数,请输入:

>>> arr = sli.rand(n)

其中 n 是要生成的随机数的数量。

备注#

上述功能都能自动检测适当的接口,并能同时处理实数和复数数据类型,将输入参数传递给适当的底层例程。