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.ndarraycupy.ndarraytorch.Tensor

  • method – 将分解方法指定为 cuquantum.cutensornet.tensor.QRMethodcuquantum.cutensornet.tensor.SVDMethod 对象。或者,也可以提供包含 QRMethodSVDMethod 构造函数参数的 dict。如果未指定,则值将设置为默认构造的 QRMethod。请注意,SVD 和 QR 方法都以简化方式运行,类似于 numpy.linalg.svdfull_matrices=Falsenumpy.linalg.qrreduced=True

  • options – 将分解的计算选项指定为 cuquantum.cutensornet.tensor.DecompositionOptions 对象。或者,也可以提供包含 DecompositionOptions 构造函数参数的 dict。如果未指定,则值将设置为默认构造的 DecompositionOptions

  • stream – 提供用于分解的 CUDA 流。可接受的输入包括 cudaStream_t(作为 Python int)、cupy.cuda.Streamtorch.cuda.Stream。如果未提供流,将使用当前流。

  • return_info – 如果为 true,则将通过 cuquantum.cutensornet.tensor.SVDInfo 对象返回有关分解的信息。目前,此选项仅支持 SVD 分解(通过 method 指定)。

返回

  • 对于 QR 分解(默认),输出张量 Q 和 R(类 ndarray 对象)与输入操作数类型相同,并且位于同一设备上,作为分解结果返回。

  • 对于 SVD 分解,如果 return_infoFalse,则返回 3 元组的输出张量 U、S 和 V(类 ndarray 对象),它们与输入操作数类型相同,作为分解结果。如果 return_infoTrue,则返回 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]。结果 qr 是 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 操作数。结果 qr 是与输入操作数位于同一设备上的 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 操作数。结果 qr 是与输入操作数位于同一设备 (dev) 上的 PyTorch 张量

>>> import torch
>>> dev = 0
>>> T = torch.rand(4,4,6,6, device=f'cuda:{dev}')
>>> q, r = decompose('ijab->ika,kjb', T)