{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"# 变分量子奇异值分解\n",
"\n",
" Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概览\n",
"\n",
"在本教程中,我们一起学习下经典奇异值分解(singular value decomposition, SVD)的概念以及我们自主研发的量子神经网络版本的量子奇异值分解(variational quantum SVD, VQSVD)[1] 是如何运作的。主体部分包括两个具体案例:\n",
"- 分解随机生成的 $8\\times8$ 复数矩阵;\n",
"- 应用在图像压缩上的效果。\n",
"\n",
"## 背景\n",
"\n",
"奇异值分解有非常多的应用,包括主成分分析(principal component analysis, PCA)、求解线性方程组和推荐系统。其主要任务是给定一个复数矩阵 $M \\in \\mathbb{C}^{m \\times n}$, 找到如下的分解形式:$M = UDV^\\dagger$。其中 $U_{m\\times m}$ 和 $V^\\dagger_{n\\times n}$ 是酉矩阵(Unitary matrix), 满足性质 $UU^\\dagger = VV^\\dagger = I$。 \n",
"\n",
"- 矩阵 $U$ 的列向量 $|u_j\\rangle$ 被称为左奇异向量(left singular vectors), $\\{|u_j\\rangle\\}_{j=1}^{m}$ 组成一组正交向量基。这些列向量本质上是矩阵 $MM^\\dagger$ 的本征向量。\n",
"- 类似的,矩阵 $V$ 的列向量 $\\{|v_j\\rangle\\}_{j=1}^{n}$ 是 $M^\\dagger M$ 的本征向量也组成一组正交向量基。\n",
"- 中间矩阵 $D_{m\\times n}$ 的对角元素上存储着由大到小排列的奇异值 $d_j$。 \n",
"\n",
"我们不妨先来看个简单的例子(为了方便讨论,我们假设以下出现的 $M$ 都是方阵):\n",
"\n",
"$$\n",
"M = 2*X\\otimes Z + 6*Z\\otimes X + 3*I\\otimes I = \n",
"\\begin{bmatrix} \n",
"3 &6 &2 &0 \\\\\n",
"6 &3 &0 &-2 \\\\\n",
"2 &0 &3 &-6 \\\\\n",
"0 &-2 &-6 &3 \n",
"\\end{bmatrix}, \\tag{1}\n",
"$$\n",
"\n",
"那么该矩阵的奇异值分解可表示为:\n",
"\n",
"$$\n",
"M = UDV^\\dagger = \n",
"\\frac{1}{2}\n",
"\\begin{bmatrix} \n",
"-1 &-1 &1 &1 \\\\\n",
"-1 &-1 &-1 &-1 \\\\\n",
"-1 &1 &-1 &1 \\\\\n",
"1 &-1 &-1 &1 \n",
"\\end{bmatrix}\n",
"\\begin{bmatrix} \n",
"11 &0 &0 &0 \\\\\n",
"0 &7 &0 &0 \\\\\n",
"0 &0 &5 &0 \\\\\n",
"0 &0 &0 &1 \n",
"\\end{bmatrix}\n",
"\\frac{1}{2}\n",
"\\begin{bmatrix} \n",
"-1 &-1 &-1 &-1 \\\\\n",
"-1 &-1 &1 &1 \\\\\n",
"-1 &1 &1 &-1 \\\\\n",
"1 &-1 &1 &-1 \n",
"\\end{bmatrix}. \\tag{2}\n",
"$$\n",
"\n",
"我们通过下面几行代码引入必要的 library和 package。\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 154,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.433321Z",
"start_time": "2021-03-09T03:47:28.730090Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"from numpy import pi as PI\n",
"from matplotlib import pyplot as plt\n",
"from scipy.stats import unitary_group\n",
"from scipy.linalg import norm\n",
"\n",
"import paddle\n",
"from paddle_quantum.ansatz import Circuit\n",
"from paddle_quantum.linalg import dagger"
]
},
{
"cell_type": "code",
"execution_count": 155,
"metadata": {},
"outputs": [],
"source": [
"# 画出优化过程中的学习曲线\n",
"def loss_plot(loss):\n",
" '''\n",
" loss is a list, this function plots loss over iteration\n",
" '''\n",
" plt.plot(list(range(1, len(loss)+1)), loss)\n",
" plt.xlabel('iteration')\n",
" plt.ylabel('loss')\n",
" plt.title('Loss Over Iteration')\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 经典奇异值分解\n",
"\n",
"那么在了解一些简单的数学背景之后, 我们来学习下如何用 Numpy 完成矩阵的奇异值分解。"
]
},
{
"cell_type": "code",
"execution_count": 156,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.469286Z",
"start_time": "2021-03-09T03:47:34.440399Z"
},
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"我们想要分解的矩阵 M 是:\n",
"[[ 3.+0.j 6.+0.j 2.+0.j 0.+0.j]\n",
" [ 6.+0.j 3.+0.j 0.+0.j -2.+0.j]\n",
" [ 2.+0.j 0.+0.j 3.+0.j -6.+0.j]\n",
" [ 0.+0.j -2.+0.j -6.+0.j 3.+0.j]]\n"
]
}
],
"source": [
"# 生成矩阵 M\n",
"def M_generator():\n",
" I = np.array([[1, 0], [0, 1]])\n",
" Z = np.array([[1, 0], [0, -1]])\n",
" X = np.array([[0, 1], [1, 0]])\n",
" Y = np.array([[0, -1j], [1j, 0]])\n",
" M = 2 *np.kron(X, Z) + 6 * np.kron(Z, X) + 3 * np.kron(I, I)\n",
" return M.astype('complex64')\n",
"\n",
"print('我们想要分解的矩阵 M 是:')\n",
"print(M_generator())"
]
},
{
"cell_type": "code",
"execution_count": 157,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.541244Z",
"start_time": "2021-03-09T03:47:34.489833Z"
},
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"矩阵的奇异值从大到小分别是:\n",
"[11. 7. 5. 1.]\n",
"分解出的酉矩阵 U 是:\n",
"[[-0.5+0.j -0.5+0.j 0.5+0.j 0.5+0.j]\n",
" [-0.5+0.j -0.5+0.j -0.5+0.j -0.5+0.j]\n",
" [-0.5+0.j 0.5+0.j -0.5+0.j 0.5+0.j]\n",
" [ 0.5+0.j -0.5+0.j -0.5+0.j 0.5+0.j]]\n",
"分解出的酉矩阵 V_dagger 是:\n",
"[[-0.5+0.j -0.5+0.j -0.5+0.j 0.5+0.j]\n",
" [-0.5+0.j -0.5+0.j 0.5+0.j -0.5+0.j]\n",
" [-0.5+0.j 0.5+0.j 0.5+0.j 0.5+0.j]\n",
" [-0.5+0.j 0.5+0.j -0.5+0.j -0.5+0.j]]\n"
]
}
],
"source": [
"# 我们只需要以下一行代码就可以完成 SVD \n",
"U, D, V_dagger = np.linalg.svd(M_generator(), full_matrices=True)\n",
"\n",
"# 打印分解结果\n",
"print(\"矩阵的奇异值从大到小分别是:\")\n",
"print(D)\n",
"print(\"分解出的酉矩阵 U 是:\")\n",
"print(U)\n",
"print(\"分解出的酉矩阵 V_dagger 是:\")\n",
"print(V_dagger)"
]
},
{
"cell_type": "code",
"execution_count": 158,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.600873Z",
"start_time": "2021-03-09T03:47:34.565570Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[ 3.+0.j 6.+0.j 2.+0.j 0.+0.j]\n",
" [ 6.+0.j 3.+0.j 0.+0.j -2.+0.j]\n",
" [ 2.+0.j 0.+0.j 3.+0.j -6.+0.j]\n",
" [ 0.+0.j -2.+0.j -6.+0.j 3.+0.j]]\n"
]
}
],
"source": [
"# 再组装回去,能不能复原矩阵?\n",
"M_reconst = np.matmul(U, np.matmul(np.diag(D), V_dagger))\n",
"print(M_reconst)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"那当然是可以复原成原来的矩阵 $M$ 的!读者也可以自行修改矩阵,试试看不是方阵的情况。\n",
"\n",
"---\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 量子奇异值分解\n",
"\n",
"接下来我们来看看量子版本的奇异值分解是怎么一回事。简单的说,我们把矩阵分解这一问题巧妙的转换成了优化问题。通过以下四个步骤:\n",
"\n",
"- 准备一组正交向量基 $\\{|\\psi_j\\rangle\\}$, 不妨直接取计算基 $\\{ |000\\rangle, |001\\rangle,\\cdots |111\\rangle\\}$ (这是3量子比特的情形)\n",
"- 准备两个参数化的量子神经网络 $U(\\theta)$ 和 $V(\\phi)$ 分别用来学习左/右奇异向量\n",
"- 利用量子神经网络估算奇异值 $m_j = \\text{Re}\\langle\\psi_j|U(\\theta)^{\\dagger} M V(\\phi)|\\psi_j\\rangle$\n",
"- 设计损失函数并且利用飞桨来优化\n",
"\n",
"$$\n",
"L(\\theta,\\phi) = \\sum_{j=1}^T q_j\\times \\text{Re} \\langle\\psi_j|U(\\theta)^{\\dagger} M V(\\phi)|\\psi_j\\rangle, \\tag{3}\n",
"$$\n",
"\n",
"其中 $q_1>\\cdots>q_T>0$ 是可以调节的权重(超参数), $T$ 表示我们想要学习到的阶数(rank)或者可以解释为总共要学习得到的奇异值个数。\n",
"\n",
"\n",
"\n",
"### 案例1:分解随机生成的 $8\\times8$ 复数矩阵\n",
"\n",
"接着我们来看一个具体的例子,这可以更好的解释整体流程。"
]
},
{
"cell_type": "code",
"execution_count": 159,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.656799Z",
"start_time": "2021-03-09T03:47:34.620380Z"
},
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"我们想要分解的矩阵 M 是:\n",
"[[6.+1.j 3.+9.j 7.+3.j 4.+7.j 6.+6.j 9.+8.j 2.+7.j 6.+4.j]\n",
" [7.+1.j 4.+4.j 3.+7.j 7.+9.j 7.+8.j 2.+8.j 5.+0.j 4.+8.j]\n",
" [1.+6.j 7.+8.j 5.+7.j 1.+0.j 4.+7.j 0.+7.j 9.+2.j 5.+0.j]\n",
" [8.+7.j 0.+2.j 9.+2.j 2.+0.j 6.+4.j 3.+9.j 8.+6.j 2.+9.j]\n",
" [4.+8.j 2.+6.j 6.+8.j 4.+7.j 8.+1.j 6.+0.j 1.+6.j 3.+6.j]\n",
" [8.+7.j 1.+4.j 9.+2.j 8.+7.j 9.+5.j 4.+2.j 1.+0.j 3.+2.j]\n",
" [6.+4.j 7.+2.j 2.+0.j 0.+4.j 3.+9.j 1.+6.j 7.+6.j 3.+8.j]\n",
" [1.+9.j 5.+9.j 5.+2.j 9.+6.j 3.+0.j 5.+3.j 1.+3.j 9.+4.j]]\n",
"矩阵的奇异值从大到小分别是:\n",
"[54.83484985 19.18141073 14.98866247 11.61419557 10.15927045 7.60223249\n",
" 5.81040539 3.30116001]\n"
]
}
],
"source": [
"# 先固定随机种子, 为了能够复现结果\n",
"np.random.seed(42)\n",
"\n",
"# 设置量子比特数量,确定希尔伯特空间的维度\n",
"N = 3\n",
"\n",
"# 制作随机矩阵生成器\n",
"def random_M_generator():\n",
" M = np.random.randint(10, size = (2**N, 2**N)) + 1j*np.random.randint(10, size = (2**N, 2**N))\n",
" M1 = np.random.randint(10, size = (2**N, 2**N)) \n",
" return M\n",
"\n",
"M = random_M_generator()\n",
"M_err = np.copy(M)\n",
"\n",
"# 打印结果\n",
"print('我们想要分解的矩阵 M 是:')\n",
"print(M)\n",
"\n",
"U, D, V_dagger = np.linalg.svd(M, full_matrices=True)\n",
"print(\"矩阵的奇异值从大到小分别是:\")\n",
"print(D)"
]
},
{
"cell_type": "code",
"execution_count": 160,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.692350Z",
"start_time": "2021-03-09T03:47:34.671114Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"选取的等差权重为:\n",
"[24.+0.j 21.+0.j 18.+0.j 15.+0.j 12.+0.j 9.+0.j 6.+0.j 3.+0.j]\n"
]
}
],
"source": [
"# 超参数设置\n",
"RANK = 8 # 设置想要学习的阶数\n",
"ITR = 100 # 迭代次数\n",
"LR = 0.02 # 学习速率\n",
"SEED = 14 # 随机数种子\n",
"\n",
"# 设置等差的学习权重\n",
"weight = np.arange(3 * RANK, 0, -3).astype('complex128')\n",
"print('选取的等差权重为:')\n",
"print(weight)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"我们搭建如下的量子神经网络结构:"
]
},
{
"cell_type": "code",
"execution_count": 161,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:47:34.725007Z",
"start_time": "2021-03-09T03:47:34.702861Z"
}
},
"outputs": [],
"source": [
"# 设置电路参数\n",
"num_qubits = 3 # 量子比特数量\n",
"cir_depth = 20 # 电路深度"
]
},
{
"cell_type": "code",
"execution_count": 162,
"metadata": {},
"outputs": [],
"source": [
"# 定义量子神经网络\n",
"def U_theta(num_qubits: int, depth: int) -> Circuit:\n",
"\n",
" # 用 Circuit 初始化网络\n",
" cir = Circuit(num_qubits)\n",
" \n",
" # 搭建层级结构\n",
" for _ in range(depth):\n",
" cir.ry()\n",
" cir.rz()\n",
" cir.cnot()\n",
"\n",
" return cir"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"接着我们来完成算法的主体部分:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 163,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:49:24.011232Z",
"start_time": "2021-03-09T03:47:40.712257Z"
}
},
"outputs": [],
"source": [
"class VQSVD():\n",
" def __init__(self, matrix: np.ndarray, weights: np.ndarray, num_qubits: int, depth: int, rank: int, lr: float, itr: int, seed: int):\n",
" \n",
" # 超参数\n",
" self.rank = rank\n",
" self.lr = lr\n",
" self.itr = itr\n",
" \n",
" paddle.seed(seed)\n",
" \n",
" # 创建用来学习 U_dagger 的量子神经网络\n",
" self.cir_U = U_theta(num_qubits, depth)\n",
" \n",
" # 创建用来学习 V_dagger 的量子神经网络\n",
" self.cir_V = U_theta(num_qubits, depth)\n",
" \n",
" # 将 Numpy array 转换成 Paddle 支持的 Tensor\n",
" self.M = paddle.to_tensor(matrix)\n",
" self.weight = paddle.to_tensor(weights)\n",
" \n",
" # 定义损失函数\n",
" def loss_func(self):\n",
" # 获取量子神经网络的酉矩阵表示\n",
" U = self.cir_U.unitary_matrix()\n",
" V = self.cir_V.unitary_matrix()\n",
" \n",
" # 初始化损失函数和奇异值存储器\n",
" loss = paddle.to_tensor(0.0)\n",
" singular_values = np.zeros(self.rank)\n",
"\n",
" for i in range(self.rank):\n",
" loss -= paddle.real(self.weight)[i] * paddle.real(dagger(U) @ self.M @ V)[i][i]\n",
" singular_values[i] = paddle.real(dagger(U) @ self.M @ V)[i][i].numpy()\n",
" \n",
" return loss, singular_values\n",
" \n",
" def get_matrix_U(self):\n",
" return self.cir_U.unitary_matrix()\n",
" \n",
" def get_matrix_V(self):\n",
" return self.cir_V.unitary_matrix()\n",
"\n",
" # 训练 VQSVD 网络\n",
" def train(self):\n",
" loss_list, singular_value_list = [], []\n",
" optimizer = paddle.optimizer.Adam(learning_rate=self.lr, parameters=self.cir_U.parameters()+self.cir_V.parameters())\n",
" for itr in range(self.itr):\n",
" loss, singular_values = self.loss_func()\n",
" loss.backward()\n",
" optimizer.minimize(loss)\n",
" optimizer.clear_grad()\n",
" loss_list.append(loss.numpy()[0])\n",
" singular_value_list.append(singular_values)\n",
" if itr% 10 == 0:\n",
" print('iter:', itr,'loss:','%.4f'% loss.numpy()[0])\n",
"\n",
" return loss_list, singular_value_list"
]
},
{
"cell_type": "code",
"execution_count": 164,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"iter: 0 loss: -88.4531\n",
"iter: 10 loss: -1795.0786\n",
"iter: 20 loss: -2059.0496\n",
"iter: 30 loss: -2202.6445\n",
"iter: 40 loss: -2269.9832\n",
"iter: 50 loss: -2304.1875\n",
"iter: 60 loss: -2320.8447\n",
"iter: 70 loss: -2331.9180\n",
"iter: 80 loss: -2340.2454\n",
"iter: 90 loss: -2348.0549\n"
]
},
{
"data": {
"image/png": "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",
"text/plain": [
"