cuquantum.cutensornet.tensor.decompose¶
- cuquantum.cutensornet.tensor.decompose(subscripts, operand, *, method=None, options=None, stream=None, return_info=False)[源代码]¶
基于
subscripts
描述的表达式,对操作数执行张量分解。该表达式采用与爱因斯坦求和 (einsum) 表达式类似的符号,但顺序相反。同时,输入现在是单个操作数,而输出包含两个或三个张量。与 einsum 表达式不同,所有输入和输出操作数都必须显式指定模式标签。
有关详细说明,请参见注释和示例。另请参见 张量分解。
- 参数
subscripts – 定义分解的模式标签(下标),以逗号分隔的字符序列表示。表达式中允许使用 Unicode 字符,从而扩展可以指定的张量的秩(维度)。
operand – 类 ndarray 张量对象。当前支持的类型为
numpy.ndarray
、cupy.ndarray
和torch.Tensor
。method – 将分解方法指定为
cuquantum.cutensornet.tensor.QRMethod
或cuquantum.cutensornet.tensor.SVDMethod
对象。或者,也可以提供包含QRMethod
或SVDMethod
构造函数参数的dict
。如果未指定,则值将设置为默认构造的QRMethod
。请注意,SVD 和 QR 方法都以简化方式运行,类似于numpy.linalg.svd
的full_matrices=False
和numpy.linalg.qr
的reduced=True
。options – 将分解的计算选项指定为
cuquantum.cutensornet.tensor.DecompositionOptions
对象。或者,也可以提供包含DecompositionOptions
构造函数参数的dict
。如果未指定,则值将设置为默认构造的DecompositionOptions
。stream – 提供用于分解的 CUDA 流。可接受的输入包括
cudaStream_t
(作为 Pythonint
)、cupy.cuda.Stream
和torch.cuda.Stream
。如果未提供流,将使用当前流。return_info – 如果为 true,则将通过
cuquantum.cutensornet.tensor.SVDInfo
对象返回有关分解的信息。目前,此选项仅支持 SVD 分解(通过method
指定)。
- 返回
对于 QR 分解(默认),输出张量 Q 和 R(类 ndarray 对象)与输入操作数类型相同,并且位于同一设备上,作为分解结果返回。
对于 SVD 分解,如果
return_info
为False
,则返回 3 元组的输出张量 U、S 和 V(类 ndarray 对象),它们与输入操作数类型相同,作为分解结果。如果return_info
为True
,则返回 4 元组的输出张量 U、S、V 和一个dict
对象,其中包含有关分解的信息。请注意,根据cuquantum.cutensornet.tensor.SVDMethod.partition
的选择,返回的 S 操作数可能为None
。另请参见partition
。
- 返回类型
根据
method
中指定的分解方法,返回的结果可能会有所不同- 引发
MemoryLimitExceeded – 执行操作所需的内存大于
options.memory_limit
分解表达式采用与 einsum 表达式类似的符号。
subscripts
字符串是下标标签列表,其中每个标签都引用相应操作数的一种模式。下标标签用逗号或标识符->
分隔。标识符->
之前的下标标签被视为输入,之后的下标标签被视为输出。下面总结了 SVD 和 QR 分解对下标的要求对于 SVD 和 QR 分解,下标字符串应恰好包含一个输入和两个输出(SVD 情况中不需要
s
的模式)。预计在两个输出模式标签中存在一个且仅有一个相同的模式。
反转后,分解下标会生成有效的 einsum 下标,该下标可以指定输出的收缩以重现输入(SVD 排除
s
的模式)。
示例
>>> # equivalent: >>> # q, r = numpy.linalg.qr(a) >>> q, r = tensor.decompose('ij->ik,kj', a)
>>> # equivalent: >>> # u, s, v = numpy.linalg.svd(a, full_matrices=False) >>> u, s, v = tensor.decompose('ij->ik,kj', a, method=tensor.SVDMethod())
对于推广到多维张量(此处
a
是秩为 4 的张量)>>> u, s, v = tensor.decompose('ijab->ixb,jax', a, method=tensor.SVDMethod()) >>> # u is unitary >>> identity = cuquantum.contract('ixb,iyb->xy', u, u.conj()) >>> # re-construct the tensor a by inverting the expression >>> a_reconstructed = cuquantum.contract('ixb,x,jax->ijab', u, s, v)
通过省略号表示法支持某些情况下的广播。可以在输入中添加省略号,以表示标签中未明确指定的所有模式。省略号必须也出现在其中一个输出中,以指示表示的模式将全部分区到哪个输出上。
示例
给定一个秩为 6 的操作数
a
>>> # equivalent: >>> # q, r = tensor.decompose('ijabcd->ixj,abcdx', a) >>> q, r = tensor.decompose('ij...->ixj,...x', a)
注意
建议用户自行维护库句柄,以减少上下文初始化时间
from cuquantum import cutensornet as cutn from cuquantum.cutensornet.tensor import decompose, QRMethod handle = cutn.create() q, r = decompose(..., method=QRMethod(), options={"handle": handle}, ...) # ... the same handle can be reused for further calls ... # when it's done, remember to destroy the handle cutn.destroy(handle)
下面我们给出更多教学示例。
示例
使用 NumPy 操作数
>>> from cuquantum.cutensornet.tensor import decompose, SVDMethod >>> import numpy as np >>> T = np.random.random((4,4,6,6))
执行张量 QR 分解,使得 T[i,j,a,b] = Q[i,k,a] R[k,j,b]。结果
q
和r
是 NumPy ndarray(计算在 GPU 上执行)>>> q, r = decompose('ijab->ika,kjb', T)
执行精确张量 SVD 分解,使得 T[i,j,a,b] = U[i,k,a] S[k] V[k,j,b]
>>> u, s, v = decompose('ijab->ika,kjb', T, method=SVDMethod())
执行精确张量 SVD 分解,使得 T[i,j,a,b] = US[i,k,a] V[k,j,b],其中 US[i,k,a] 表示 U[i,k,a] 和 S[k] 的乘积
>>> us, _, v = decompose('ijab->ika,kjb', T, method=SVDMethod(partition="U"))
执行精确张量 SVD 分解,使得 T[i,j,a,b] = U[i,k,a] S[k] V[k,j,b],然后将输出奇异值的 L2 范数归一化为 1
>>> u, s_normalized, v = decompose('ijab->ika,kjb', T, method=SVDMethod(normalization="L2") ) >>> print(np.linalg.norm(s_normalized)) # 1.0
执行截断 SVD 分解,最多保留 8 个奇异值。同时,通过
return_info=True
请求 SVD 截断的运行时信息。>>> u, s, v, info = decompose('ijab->ika,kjb', T, method=SVDMethod(max_extent=8), return_info=True) >>> print(s.shape) # (8,) >>> print(info)
我们还可以执行满足以下所有要求的截断 SVD 分解
剩余奇异值的数量不应超过 8 个。
剩余奇异值均大于 0.01
剩余奇异值均大于 0.1 * 最大奇异值
剩余奇异值(在截断和归一化之后)的 L1 范数归一化为 1。
剩余奇异值(在截断和归一化之后)平均分配到 U 和 V 上
>>> method = {"max_extent": 8, ... "abs_cutoff": 0.01, ... "rel_cutoff": 0.1, ... "normalization": "L1", ... "partition": "UV"} >>> svd_method = SVDMethod(**method) >>> us, _, sv, info = decompose('ijab->ika,kjb', T, method=svd_method, return_info=True)
或者,选项可以作为
dict
对象提供>>> us, _, sv, info = tensor_decompose('ijab->ika,kjb', T, method=method, return_info=True)
使用 CuPy 操作数。结果
q
和r
是与输入操作数位于同一设备上的 CuPy ndarray,并且dev
是您希望用于存储张量和执行分解的系统上的任何有效设备 ID>>> import cupy >>> dev = 0 >>> with cupy.cuda.Device(dev): ... T = cupy.random.random((4,4,6,6)) >>> q, r = decompose('ijab->ika,kjb', T)
使用 PyTorch 操作数。结果
q
和r
是与输入操作数位于同一设备 (dev
) 上的 PyTorch 张量>>> import torch >>> dev = 0 >>> T = torch.rand(4,4,6,6, device=f'cuda:{dev}') >>> q, r = decompose('ijab->ika,kjb', T)