提交 01bee618 编写于 作者: Q Quleaf

update to v1.2.0

上级 166e0cbd
......@@ -18,25 +18,53 @@ English | [简体中文](README_CN.md)
- [Copyright and License](#copyright-and-license)
- [References](#references)
Paddle Quantum (量桨) is a quantum machine learning (QML) toolkit developed based on Baidu PaddlePaddle. It provides a platform to construct and train quantum neural networks (QNNs) with easy-to-use QML development kits suporting combinatorial optimization, quantum chemistry and other cutting-edge quantum applications, making PaddlePaddle the first and only deep learning framework in China that supports quantum machine learning.
[Paddle Quantum (量桨)](https://qml.baidu.com/) is a quantum machine learning (QML) toolkit developed based on Baidu PaddlePaddle. It provides a platform to construct and train quantum neural networks (QNNs) with easy-to-use QML development kits suporting combinatorial optimization, quantum chemistry and other cutting-edge quantum applications, making PaddlePaddle the first and only deep learning framework in China that supports quantum machine learning.
<p align="center">
<a href="https://qml.baidu.com/">
<img width=80% src="https://release-data.cdn.bcebos.com/Paddle%20Quantum.png">
</a>
</p>
<p align="center">
<!-- docs -->
<a href="https://qml.baidu.com/api/paddle_quantum.circuit.html">
<img src="https://img.shields.io/badge/docs-link-green.svg?style=flat-square&logo=read-the-docs"/>
</a>
<!-- PyPI -->
<a href="https://pypi.org/project/paddle-quantum/">
<img src="https://img.shields.io/badge/pypi-v1.2.0-orange.svg?style=flat-square&logo=pypi"/>
</a>
<!-- Python -->
<a href="https://www.python.org/">
<img src="https://img.shields.io/badge/Python-3.6+-blue.svg?style=flat-square&logo=python"/>
</a>
<!-- License -->
<a href="./LICENSE">
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square&logo=apache"/>
</a>
<!-- Platform -->
<a href="https://github.com/PaddlePaddle/Quantum">
<img src="https://img.shields.io/badge/OS-MacOS%20|%20Windows%20|%20Linux-lightgrey.svg?style=flat-square"/>
</a>
</p>
![](https://release-data.cdn.bcebos.com/Paddle%20Quantum.png)
Paddle Quantum aims at establishing a bridge between artificial intelligence (AI) and quantum computing (QC). It has been utilized for developing several quantum machine learning applications. With the PaddlePaddle deep learning platform empowering QC, Paddle Quantum provides strong support for scientific research community and developers in the field to easily develop QML applications. Moreover, it provides a learning platform for quantum computing enthusiasts.
## Features
- Easy-to-use
- Build quantum neural networks efficiently
- Various quantum neural network templates
- 10+ QML algorithm tutorials
- Scalability
- Support universal quantum circuit model
- Provide multiple optimization tools and GPU mode
- High-performance simulator that supports more than 20 qubits
- Many online learning resources (16+ tutorials)
- High efficiency in building QNN with various QNN templates
- Automatic differentiation
- Versatile
- Multiple optimization tools and GPU mode
- Simulation with 25+ qubits
- Featured Toolkits
- Provides computational toolboxes in cutting-edge fields such as combinatorial optimization and quantum chemistry
- Self-innovated quantum machine learning algorithms
- Toolboxes for Chemistry & Optimization
- LOCCNet for distributed quantum information processing
- Self-developed QML algorithms
## Install
......@@ -72,7 +100,7 @@ pip install openfermion==0.11.0
pip install openfermionpyscf==0.4
```
### Run programs
### Run example
Now, you can try to run a program to verify whether Paddle Quantum has been installed successfully. Here we take quantum approximate optimization algorithm (QAOA) as an example.
......@@ -97,20 +125,26 @@ python main.py
### Tutorials
We provide tutorials covering combinatorial optimization, quantum chemistry, quantum classification and other popular QML research topics. Each tutorial currently supports reading on our website and running Jupyter Notebooks locally. For interested developers, we recommend them to download Jupyter Notebooks and play around with it. Here is the tutorial list,
1. [Quantum Approximation Optimization Algorithm (QAOA)](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/QAOA)
2. [Variational Quantum Eigensolver (VQE)](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/VQE)
3. [Quantum Classifier](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/Q-Classifier)
4. [The Barren Plateaus Phenomenon on Quantum Neural Networks (Barren Plateaus)](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/Barren)
5. [Quantum Autoencoder](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/Q-Autoencoder)
6. [Quantum GAN](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/Q-GAN)
7. [Subspace Search-Quantum Variational Quantum Eigensolver (SSVQE)](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/SSVQE)
8. [Variational Quantum State Diagonalization (VQSD)](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/VQSD)
9. [Gibbs State Preparation](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/Gibbs)
10. [Variational Quantum Singular Value Decomposition (VQSVD)](https://github.com/PaddlePaddle/Quantum/blob/master/tutorial/VQSVD)
In addition, Paddle Quantum supports QNN training on GPU. For users who want to get into more details, please check out the tutorial [Use Paddle Quantum on GPU](https://github.com/PaddlePaddle/Quantum/blob/master/introduction/PaddleQuantum_GPU_EN.ipynb).
We provide tutorials covering combinatorial optimization, quantum chemistry, quantum classification, quantum entanglement manipulation and other popular QML research topics. Each tutorial currently supports reading on our website and running Jupyter Notebooks locally. For interested developers, we recommend them to download Jupyter Notebooks and play around with it. Here is the tutorial list,
1. [Quantum Approximation Optimization Algorithm (QAOA)](./tutorial/QAOA)
2. [Variational Quantum Eigensolver (VQE)](./tutorial/VQE)
3. [Quantum Classifier](./tutorial/Q-Classifier)
4. [The Barren Plateaus Phenomenon on Quantum Neural Networks (Barren Plateaus)](./tutorial/Barren)
5. [Quantum Autoencoder](./tutorial/Q-Autoencoder)
6. [Quantum GAN](./tutorial/Q-GAN)
7. [Subspace Search-Quantum Variational Quantum Eigensolver (SSVQE)](./tutorial/SSVQE)
8. [Variational Quantum State Diagonalization (VQSD)](./tutorial/VQSD)
9. [Gibbs State Preparation](./tutorial/Gibbs)
10. [Variational Quantum Singular Value Decomposition (VQSVD)](./tutorial/VQSVD)
11. [Local Operations and Classical Communication in QNN (LOCCNet)](./tutorial/LOCC/LOCCNET_Tutorial_EN.ipynb)
12. [Entanglement Distillation -- the BBPSSW protocol](./tutorial/LOCC)
13. [Entanglement Distillation -- the DEJMPS protocol](./tutorial/LOCC)
14. [Entanglement Distillation -- Protocol design with LOCCNet](./tutorial/LOCC)
15. [Quantum Teleportation](./tutorial/LOCC)
16. [Quantum State Discrimination](./tutorial/LOCC)
With the latest LOCCNet module, Paddle Quantum can efficiently simulate distributed quantum information processing tasks. Interested readers can start with this [tutorial on LOCCNet](./tutorial/LOCC/LOCCNET_Tutorial_EN.ipynb). In addition, Paddle Quantum supports QNN training on GPU. For users who want to get into more details, please check out the tutorial [Use Paddle Quantum on GPU](./introduction/PaddleQuantum_GPU_EN.ipynb).
### API documentation
......
......@@ -19,24 +19,51 @@
- [Copyright and License](#copyright-and-license)
- [References](#references)
Paddle Quantum(量桨)是基于百度飞桨开发的量子机器学习工具集,支持量子神经网络的搭建与训练,提供易用的量子机器学习开发套件与量子优化、量子化学等前沿量子应用工具集,使得百度飞桨也因此成为国内首个目前也是唯一一个支持量子机器学习的深度学习框架。
![](https://release-data.cdn.bcebos.com/Paddle%20Quantum.png)
[Paddle Quantum(量桨)](https://qml.baidu.com/)是基于百度飞桨开发的量子机器学习工具集,支持量子神经网络的搭建与训练,提供易用的量子机器学习开发套件与量子优化、量子化学等前沿量子应用工具集,使得百度飞桨也因此成为国内首个目前也是唯一一个支持量子机器学习的深度学习框架。
<p align="center">
<a href="https://qml.baidu.com/">
<img width=80% src="https://release-data.cdn.bcebos.com/Paddle%20Quantum.png">
</a>
</p>
<p align="center">
<!-- docs -->
<a href="https://qml.baidu.com/api/paddle_quantum.circuit.html">
<img src="https://img.shields.io/badge/docs-link-green.svg?style=flat-square&logo=read-the-docs"/>
</a>
<!-- PyPI -->
<a href="https://pypi.org/project/paddle-quantum/">
<img src="https://img.shields.io/badge/pypi-v1.2.0-orange.svg?style=flat-square&logo=pypi"/>
</a>
<!-- Python -->
<a href="https://www.python.org/">
<img src="https://img.shields.io/badge/Python-3.6+-blue.svg?style=flat-square&logo=python"/>
</a>
<!-- License -->
<a href="./LICENSE">
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square&logo=apache"/>
</a>
<!-- Platform -->
<a href="https://github.com/PaddlePaddle/Quantum">
<img src="https://img.shields.io/badge/OS-MacOS%20|%20Windows%20|%20Linux-lightgrey.svg?style=flat-square"/>
</a>
</p>
量桨建立起了人工智能与量子计算的桥梁,不但可以快速实现量子神经网络的搭建与训练,还提供易用的量子机器学习开发套件与量子优化、量子化学等前沿量子应用工具集,并提供多项自研量子机器学习应用。通过百度飞桨深度学习平台赋能量子计算,量桨为领域内的科研人员以及开发者便捷地开发量子人工智能的应用提供了强有力的支撑,同时也为广大量子计算爱好者提供了一条可行的学习途径。
## 特色
- 易用性
- 高效搭建量子神经网络
- 多种量子神经网络模板
- 丰富的量子算法教程(10+用例)
- 可拓展性
- 支持通用量子电路模型
- 高性能模拟器支持20多个量子比特的模拟运算
- 提供多种优化工具和 GPU 加速
- 轻松上手
- 丰富的在线学习资源(16+ 教程案例)
- 通过模板高效搭建量子神经网络
- 自动微分框架
- 功能丰富
- 提供多种优化工具和 GPU 模式
- 高性能模拟器支持25+量子比特的模拟运算
- 特色工具集
- 提供组合优化和量子化学等前沿领域的计算工具箱
- 分布式量子信息处理模组 LOCCNet
- 自研多种量子机器学习算法
## 安装步骤
......@@ -61,11 +88,11 @@ pip install -e .
```
### 使用 openfermion 读取 xyz 描述文件
### 使用 OpenFermion 读取 .xyz 描述文件
> 仅在 macOS 和 linux 下可以使用 openfermion 读取 xyz 描述文件。
> 仅在 macOS 和 linux 下可以使用 OpenFermion 读取 .xyz 描述文件。
VQE中调用 openfermion 读取分子 xyz 文件并计算,因此需要安装 openfermion 和 openfermionpyscf。
VQE中调用 OpenFermion 读取分子 .xyz 文件并计算,因此需要安装 openfermion 和 openfermionpyscf。
```bash
pip install openfermion==0.11.0
......@@ -103,20 +130,26 @@ python main.py
Paddle Quantum(量桨)建立起了人工智能与量子计算的桥梁,为量子机器学习领域的研发提供强有力的支撑,也提供了丰富的案例供开发者学习。
在这里,我们提供了涵盖量子优化、量子化学、量子机器学习等多个领域的案例供大家学习。每个教程目前支持网页阅览和运行 Jupyter Notebook 两种方式。我们推荐用户下载 Notebook 后,本地运行进行实践。
在这里,我们提供了涵盖量子优化、量子化学、量子机器学习、量子纠缠处理等多个领域的案例供大家学习。每个教程目前支持网页阅览和运行 Jupyter Notebook 两种方式。我们推荐用户下载 Notebook 后,本地运行进行实践。
- [量子近似优化算法 (QAOA)](./tutorial/QAOA)
- [变分量子特征求解器 (VQE)](./tutorial/VQE)
- [量子神经网络的贫瘠高原效应 (Barren Plateaus)](./tutorial/Barren)
- [量子分类器 (Quantum Classifier)](./tutorial/Q-Classifier)
- [量子神经网络的贫瘠高原效应 (Barren Plateaus)](./tutorial/Barren)
- [量子变分自编码器 (Quantum Autoencoder)](./tutorial/Q-Autoencoder)
- [量子生成对抗网络 (Quantum GAN)](./tutorial/Q-GAN)
- [子空间搜索 - 量子变分特征求解器 (SSVQE)](./tutorial/SSVQE)
- [变分量子态对角化算法 (VQSD)](./tutorial/VQSD)
- [吉布斯态的制备 (Gibbs State Preparation)](./tutorial/Gibbs)
- [变分量子奇异值分解 (VQSVD)](./tutorial/VQSVD)
此外,Paddle Quantum 也支持在 GPU 上进行量子机器学习的训练,具体的方法请参考案例:[在 GPU 上使用 Paddle Quantum](./introduction/PaddleQuantum_GPU_CN.ipynb)
- [LOCC 量子神经网络](./tutorial/LOCC/LOCCNET_Tutorial_CN.ipynb)
- [纠缠蒸馏 -- BBPSSW 协议](./tutorial/LOCC)
- [纠缠蒸馏 -- DEJMPS 协议](./tutorial/LOCC)
- [纠缠蒸馏 -- LOCCNet 设计协议](./tutorial/LOCC)
- [量子隐态传输](./tutorial/LOCC)
- [量子态分辨](./tutorial/LOCC)
随着 LOCCNet 模组的推出,量桨现已支持分布式量子信息处理任务的高效模拟和开发。感兴趣的读者请参见[教程](./tutorial/LOCC/LOCCNET_Tutorial_CN.ipynb)。此外,Paddle Quantum 也支持在 GPU 上进行量子机器学习的训练,具体的方法请参考案例:[在 GPU 上使用 Paddle Quantum](./introduction/PaddleQuantum_GPU_CN.ipynb)
### API 文档
......
......@@ -1503,7 +1503,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.8"
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
......
......@@ -17,3 +17,4 @@ Paddle Quantum Library
"""
name = "paddle_quantum"
__version__= "1.2.0"
\ No newline at end of file
......@@ -43,7 +43,7 @@ __all__ = [
class UAnsatz:
r"""基于PaddlePaddle的动态图机制实现量子线路的 ``class`` 。
r"""基于 PaddlePaddle 的动态图机制实现量子线路的 ``class`` 。
用户可以通过实例化该 ``class`` 来搭建自己的量子线路。
......@@ -52,7 +52,7 @@ class UAnsatz:
"""
def __init__(self, n):
r"""UAnsatz的构造函数,用于实例化一个UAnsatz对象
r"""UAnsatz 的构造函数,用于实例化一个 UAnsatz 对象
Args:
n (int): 该线路的量子比特数
......@@ -114,6 +114,8 @@ class UAnsatz:
state = StateTranfer(state, 'u', history_ele[1], history_ele[2])
elif history_ele[0] in {'x', 'y', 'z', 'h'}:
state = StateTranfer(state, history_ele[0], history_ele[1], params=history_ele[2])
elif history_ele[0] == 'SWAP':
state = StateTranfer(state, 'SWAP', history_ele[1])
elif history_ele[0] == 'CNOT':
state = StateTranfer(state, 'CNOT', history_ele[1])
......@@ -213,6 +215,8 @@ class UAnsatz:
state = StateTranfer(state, 'u', history_ele[1], history_ele[2])
elif history_ele[0] in {'x', 'y', 'z', 'h'}:
state = StateTranfer(state, history_ele[0], history_ele[1], params=history_ele[2])
elif history_ele[0] == 'SWAP':
state = StateTranfer(state, 'SWAP', history_ele[1])
elif history_ele[0] == 'CNOT':
state = StateTranfer(state, 'CNOT', history_ele[1])
......@@ -223,7 +227,7 @@ class UAnsatz:
"""
def rx(self, theta, which_qubit):
r"""添加关于x轴的单量子比特旋转门。
r"""添加关于 x 轴的单量子比特旋转门。
其矩阵形式为:
......@@ -233,7 +237,7 @@ class UAnsatz:
Args:
theta (Variable): 旋转角度
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
.. code-block:: python
......@@ -254,7 +258,7 @@ class UAnsatz:
dygraph.to_variable(np.array([math.pi / 2]))]])
def ry(self, theta, which_qubit):
r"""添加关于y轴的单量子比特旋转门。
r"""添加关于 y 轴的单量子比特旋转门。
其矩阵形式为:
......@@ -264,7 +268,7 @@ class UAnsatz:
Args:
theta (Variable): 旋转角度
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
.. code-block:: python
......@@ -285,7 +289,7 @@ class UAnsatz:
dygraph.to_variable(np.array([0.0]))]])
def rz(self, theta, which_qubit):
r"""添加关于z轴的单量子比特旋转门。
r"""添加关于 z 轴的单量子比特旋转门。
其矩阵形式为:
......@@ -295,7 +299,7 @@ class UAnsatz:
Args:
theta (Variable): 旋转角度
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
.. code-block:: python
......@@ -316,9 +320,9 @@ class UAnsatz:
theta]])
def cnot(self, control):
r"""添加一个CNOT门。
r"""添加一个 CNOT 门。
对于2量子比特的量子线路,当 ``control`` 为 ``[0, 1]`` 时,其矩阵形式为:
对于 2 量子比特的量子线路,当 ``control`` 为 ``[0, 1]`` 时,其矩阵形式为:
.. math::
......@@ -328,7 +332,7 @@ class UAnsatz:
\end{align}
Args:
control (list): 作用在的qubit的编号,``control[0]`` 为控制位,``control[1]`` 为目标位,其值都应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
control (list): 作用在的 qubit 的编号,``control[0]`` 为控制位,``control[1]`` 为目标位,其值都应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
.. code-block:: python
......@@ -345,8 +349,36 @@ class UAnsatz:
assert control[0] != control[1], "the control qubit is the same as the target qubit"
self.__history.append(['CNOT', control])
def swap(self, control):
r"""添加一个 SWAP 门。
其矩阵形式为:
.. math::
\begin{align}
SWAP &=\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
\end{align}
Args:
control (list): 作用在的 qubit 的编号,``control[0]`` 和``control[1]`` 是想要交换的位,其值都应该在 :math:`[0, n)`范围内, :math:`n` 为该量子线路的量子比特数
.. code-block:: python
import numpy as np
from paddle import fluid
from paddle_quantum.circuit import UAnsatz
num_qubits = 2
with fluid.dygraph.guard():
cir = UAnsatz(num_qubits)
cir.swap([0, 1])
"""
assert 0 <= control[0] < self.n and 0 <= control[1] < self.n, "the qubit should >= 0 and < n(the number of qubit)"
assert control[0] != control[1], "the indices needed to be swapped should not be the same"
self.__history.append(['SWAP', control])
def x(self, which_qubit):
r"""添加单量子比特X门。
r"""添加单量子比特 X 门。
其矩阵形式为:
......@@ -371,7 +403,7 @@ class UAnsatz:
self.__history.append(['x', [which_qubit], None])
def y(self, which_qubit):
r"""添加单量子比特Y门。
r"""添加单量子比特 Y 门。
其矩阵形式为:
......@@ -380,7 +412,7 @@ class UAnsatz:
\begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix}
Args:
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
.. code-block:: python
......@@ -396,7 +428,7 @@ class UAnsatz:
self.__history.append(['y', [which_qubit], None])
def z(self, which_qubit):
r"""添加单量子比特Z门。
r"""添加单量子比特 Z 门。
其矩阵形式为:
......@@ -421,7 +453,7 @@ class UAnsatz:
self.__history.append(['z', [which_qubit], None])
def h(self, which_qubit):
r"""添加一个单量子比特的Hadamard门。
r"""添加一个单量子比特的 Hadamard 门。
其矩阵形式为:
......@@ -430,13 +462,13 @@ class UAnsatz:
H = \frac{1}{\sqrt{2}}\begin{bmatrix} 1&1\\1&-1 \end{bmatrix}
Args:
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
"""
assert 0 <= which_qubit < self.n, "the qubit should >= 0 and < n(the number of qubit)"
self.__history.append(['h', [which_qubit], None])
def s(self, which_qubit):
r"""添加一个单量子比特的S门。
r"""添加一个单量子比特的 S 门。
其矩阵形式为:
......@@ -445,7 +477,7 @@ class UAnsatz:
S = \begin{bmatrix} 1&0\\0&i \end{bmatrix}
Args:
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
"""
assert 0 <= which_qubit < self.n, "the qubit should >= 0 and < n(the number of qubit)"
self.__history.append(['u', [which_qubit], [dygraph.to_variable(np.array([0.0])),
......@@ -453,7 +485,7 @@ class UAnsatz:
dygraph.to_variable(np.array([math.pi / 2]))]])
def t(self, which_qubit):
r"""添加一个单量子比特的T门。
r"""添加一个单量子比特的 T 门。
其矩阵形式为:
......@@ -462,7 +494,7 @@ class UAnsatz:
T = \begin{bmatrix} 1&0\\0&e^\frac{i\pi}{4} \end{bmatrix}
Args:
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
"""
assert 0 <= which_qubit < self.n, "the qubit should >= 0 and < n(the number of qubit)"
self.__history.append(['u', [which_qubit], [dygraph.to_variable(np.array([0.0])),
......@@ -488,11 +520,147 @@ class UAnsatz:
theta (Variable): 旋转角度 :math:`\theta` 。
phi (Variable): 旋转角度 :math:`\phi` 。
lam (Variable): 旋转角度 :math:`\lambda` 。
which_qubit (int): 作用在的qubit的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, n)` 范围内, :math:`n` 为该量子线路的量子比特数
"""
assert 0 <= which_qubit < self.n, "the qubit should >= 0 and < n(the number of qubit)"
self.__history.append(['u', [which_qubit], [theta, phi, lam]])
def universal_2_qubit_gate(self, theta, which_qubits):
r"""添加 2-qubit 通用门,这个通用门需要 15 个参数。
Args:
theta (Variable): 2-qubit 通用门的参数,其维度为 ``(15, )``
which_qubits(list): 作用的量子比特编号
代码示例:
.. code-block:: python
import numpy as np
from paddle import fluid
from paddle_quantum.circuit import UAnsatz
n = 2
theta = np.ones(15)
with fluid.dygraph.guard():
theta = fluid.dygraph.to_variable(theta)
cir = UAnsatz(n)
cir.universal_2_qubit_gate(fluid.dygraph.to_variable(theta), [0, 1])
cir.run_state_vector()
print(cir.measure(shots = 0))
::
{'00': 0.4306256106527819, '01': 0.07994547866706268, '10': 0.07994547866706264, '11': 0.40948343201309334}
"""
assert len(theta.shape) == 1, 'The shape of theta is not right'
assert len(theta) == 15, 'This Ansatz accepts 15 parameters'
assert len(which_qubits) == 2, "You should add this gate on two qubits"
a, b = which_qubits
self.u3(theta[0], theta[1], theta[2], a)
self.u3(theta[3], theta[4], theta[5], b)
self.cnot([b, a])
self.rz(theta[6], a)
self.ry(theta[7], b)
self.cnot([a, b])
self.ry(theta[8], b)
self.cnot([b, a])
self.u3(theta[9], theta[10], theta[11], a)
self.u3(theta[12], theta[13], theta[14], b)
def __u3qg_U(self, theta, which_qubits):
r"""
用于构建 universal_3_qubit_gate
"""
self.cnot(which_qubits[1:])
self.ry(theta[0], which_qubits[1])
self.cnot(which_qubits[:2])
self.ry(theta[1], which_qubits[1])
self.cnot(which_qubits[:2])
self.cnot(which_qubits[1:])
self.h(which_qubits[2])
self.cnot([which_qubits[1], which_qubits[0]])
self.cnot([which_qubits[0], which_qubits[2]])
self.cnot(which_qubits[1:])
self.rz(theta[2], which_qubits[2])
self.cnot(which_qubits[1:])
self.cnot([which_qubits[0], which_qubits[2]])
def __u3qg_V(self, theta, which_qubits):
r"""
用于构建 universal_3_qubit_gate
"""
self.cnot([which_qubits[2], which_qubits[0]])
self.cnot(which_qubits[:2])
self.cnot([which_qubits[2], which_qubits[1]])
self.ry(theta[0], which_qubits[2])
self.cnot(which_qubits[1:])
self.ry(theta[1], which_qubits[2])
self.cnot(which_qubits[1:])
self.s(which_qubits[2])
self.cnot([which_qubits[2], which_qubits[0]])
self.cnot(which_qubits[:2])
self.cnot([which_qubits[1], which_qubits[0]])
self.h(which_qubits[2])
self.cnot([which_qubits[0], which_qubits[2]])
self.rz(theta[2], which_qubits[2])
self.cnot([which_qubits[0], which_qubits[2]])
def universal_3_qubit_gate(self, theta, which_qubits):
r"""添加 3-qubit 通用门,这个通用门需要 81 个参数。
Note:
参考: https://cds.cern.ch/record/708846/files/0401178.pdf
Args:
theta (Variable): 3-qubit 通用门的参数,其维度为 ``(81, )``
which_qubits(list): 作用的量子比特编号
代码示例:
.. code-block:: python
import numpy as np
from paddle import fluid
from paddle_quantum.circuit import UAnsatz
n = 3
theta = np.ones(81)
with fluid.dygraph.guard():
theta = fluid.dygraph.to_variable(theta)
cir = UAnsatz(n)
cir.universal_3_qubit_gate(fluid.dygraph.to_variable(theta), [0, 1, 2])
cir.run_state_vector()
print(cir.measure(shots = 0))
::
{'000': 0.06697926831547105, '001': 0.13206788591381013, '010': 0.2806525391078656, '011': 0.13821526515701105, '100': 0.1390530116439897, '101': 0.004381404333075108, '110': 0.18403296778911565, '111': 0.05461765773966483}
"""
assert len(which_qubits) == 3, "You should add this gate on three qubits"
assert len(theta) == 81, "The length of theta is supposed to be 81"
psi = fluid.layers.reshape(x=theta[: 60], shape=[4, 15])
phi = fluid.layers.reshape(x=theta[60:], shape=[7, 3])
self.universal_2_qubit_gate(psi[0], which_qubits[:2])
self.u3(phi[0][0], phi[0][1], phi[0][2], which_qubits[2])
self.__u3qg_U(phi[1], which_qubits)
self.universal_2_qubit_gate(psi[1], which_qubits[:2])
self.u3(phi[2][0], phi[2][1], phi[2][2], which_qubits[2])
self.__u3qg_V(phi[3], which_qubits)
self.universal_2_qubit_gate(psi[2], which_qubits[:2])
self.u3(phi[4][0], phi[4][1], phi[4][2], which_qubits[2])
self.__u3qg_U(phi[5], which_qubits)
self.universal_2_qubit_gate(psi[3], which_qubits[:2])
self.u3(phi[6][0], phi[6][1], phi[6][2], which_qubits[2])
"""
Measurements
"""
......@@ -563,11 +731,11 @@ class UAnsatz:
r"""对量子线路输出的量子态进行测量。
Warning:
当 ``plot`` 为 ``True`` 时,当前量子线路的量子比特数需要小于6,否则无法绘制图片,会抛出异常。
当 ``plot`` 为 ``True`` 时,当前量子线路的量子比特数需要小于 6 ,否则无法绘制图片,会抛出异常。
Args:
which_qubits (list, optional): 要测量的qubit的编号,默认全都测量
shots (int, optional): 该量子线路输出的量子态的测量次数,默认为1024次;若为0,则返回测量结果的精确概率分布
shots (int, optional): 该量子线路输出的量子态的测量次数,默认为 1024 次;若为 0,则返回测量结果的精确概率分布
plot (bool, optional): 是否绘制测量结果图,默认为 ``False`` ,即不绘制
Returns:
......@@ -641,14 +809,14 @@ class UAnsatz:
return result if not plot else self.__measure_hist(result, which_qubits, shots)
def expecval(self, H):
r"""量子线路输出的量子态关于可观测量H的期望值。
r"""量子线路输出的量子态关于可观测量 H 的期望值。
Hint:
如果想输入的可观测量的矩阵为 :math:`0.7Z\otimes X\otimes I+0.2I\otimes Z\otimes I` 。则 ``H`` 应为 ``[[0.7, 'z0,x1'], [0.2, 'z1']]`` 。
Args:
H (list): 可观测量的相关信息
Returns:
Variable: 量子线路输出的量子态关于H的期望值
Variable: 量子线路输出的量子态关于 H 的期望值
代码示例:
......@@ -717,7 +885,7 @@ class UAnsatz:
"""
def superposition_layer(self):
r"""添加一层Hadamard门。
r"""添加一层 Hadamard 门。
代码示例:
......@@ -740,7 +908,7 @@ class UAnsatz:
self.h(i)
def weak_superposition_layer(self):
r"""添加一层旋转角度为 :math:`\pi/4` 的Ry门。
r"""添加一层旋转角度为 :math:`\pi/4` 的 Ry 门。
代码示例:
......@@ -763,8 +931,8 @@ class UAnsatz:
for i in range(self.n):
self.ry(_theta, i)
def real_entangled_layer(self, theta, depth):
r"""添加 ``depth`` 层包含Ry门和CNOT门的强纠缠层。
def real_entangled_layer(self, theta, depth, which_qubits=None):
r"""添加 ``depth`` 层包含 Ry 门和 CNOT 门的强纠缠层。
Note:
这一层量子门的数学表示形式为实数酉矩阵。
......@@ -773,8 +941,9 @@ class UAnsatz:
``theta`` 的维度为 ``(depth, n, 1)``
Args:
theta (Variable): Ry门的旋转角度
theta (Variable): Ry 门的旋转角度
depth (int): 纠缠层的深度
which_qubits(list): 作用的量子比特编号
代码示例:
......@@ -789,7 +958,7 @@ class UAnsatz:
with fluid.dygraph.guard():
theta = fluid.dygraph.to_variable(theta)
cir = UAnsatz(n)
cir.real_entangled_layer(fluid.dygraph.to_variable(theta), DEPTH)
cir.real_entangled_layer(fluid.dygraph.to_variable(theta), DEPTH, [0, 1])
cir.run_state_vector()
print(cir.measure(shots = 0))
......@@ -800,18 +969,21 @@ class UAnsatz:
assert self.n > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 1, 'the shape of theta is not right'
assert theta.shape[1] == self.n, 'the shape of theta is not right'
#assert theta.shape[1] == self.n, 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
if which_qubits is None:
which_qubits = np.arange(self.n)
for repeat in range(depth):
for i in range(self.n):
self.ry(theta=theta[repeat][i][0], which_qubit=i)
for i in range(self.n - 1):
self.cnot(control=[i, i + 1])
self.cnot([self.n - 1, 0])
for i, q in enumerate(which_qubits):
self.ry(theta=theta[repeat][i][0], which_qubit=q)
for i in range(len(which_qubits) - 1):
self.cnot([which_qubits[i], which_qubits[i + 1]])
self.cnot([which_qubits[-1], which_qubits[0]])
def complex_entangled_layer(self, theta, depth):
r"""添加 ``depth`` 层包含U3门和CNOT门的强纠缠层。
def complex_entangled_layer(self, theta, depth, which_qubits=None):
r"""添加 ``depth`` 层包含 U3 门和 CNOT 门的强纠缠层。
Note:
这一层量子门的数学表示形式为复数酉矩阵。
......@@ -820,8 +992,9 @@ class UAnsatz:
``theta`` 的维度为 ``(depth, n, 3)`` ,最低维内容为对应的 ``u3`` 的参数 ``(theta, phi, lam)``
Args:
theta (Variable): U3门的旋转角度
theta (Variable): U3 门的旋转角度
depth (int): 纠缠层的深度
which_qubits(list): 作用的量子比特编号
代码示例:
......@@ -836,7 +1009,7 @@ class UAnsatz:
with fluid.dygraph.guard():
theta = fluid.dygraph.to_variable(theta)
cir = UAnsatz(n)
cir.complex_entangled_layer(fluid.dygraph.to_variable(theta), DEPTH)
cir.complex_entangled_layer(fluid.dygraph.to_variable(theta), DEPTH, [0, 1])
cir.run_state_vector()
print(cir.measure(shots = 0))
......@@ -847,65 +1020,18 @@ class UAnsatz:
assert self.n > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 3, 'the shape of theta is not right'
assert theta.shape[1] == self.n, 'the shape of theta is not right'
# assert theta.shape[1] == self.n, 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
for repeat in range(depth):
for i in range(self.n):
self.u3(theta[repeat][i][0], theta[repeat][i][1], theta[repeat][i][2], i)
for i in range(self.n - 1):
self.cnot([i, i + 1])
self.cnot([self.n - 1, 0])
def universal_2_qubit_gate(self, theta):
r"""添加2-qubit通用门,这个通用门需要15个参数。
Attention:
只适用于量子比特数为2的量子线路。
Args:
theta (Variable): 2-qubit通用门的参数,其维度为 ``(15, )``
代码示例:
.. code-block:: python
if which_qubits is None:
which_qubits = np.arange(self.n)
import numpy as np
from paddle import fluid
from paddle_quantum.circuit import UAnsatz
n = 2
theta = np.ones(15)
with fluid.dygraph.guard():
theta = fluid.dygraph.to_variable(theta)
cir = UAnsatz(n)
cir.universal_2_qubit_gate(fluid.dygraph.to_variable(theta))
cir.run_state_vector()
print(cir.measure(shots = 0))
::
{'00': 0.4306256106527819, '01': 0.07994547866706268, '10': 0.07994547866706264, '11': 0.40948343201309334}
"""
assert self.n == 2, 'It only works on 2-qubit circuit'
assert len(theta.shape) == 1, 'The shape of theta is not right'
assert len(theta) == 15, 'This Ansatz accepts 15 parameters'
self.u3(theta[0], theta[1], theta[2], 0)
self.u3(theta[3], theta[4], theta[5], 1)
self.cnot([1, 0])
self.rz(theta[6], 0)
self.ry(theta[7], 1)
self.cnot([0, 1])
self.ry(theta[8], 1)
self.cnot([1, 0])
self.u3(theta[9], theta[10], theta[11], 0)
self.u3(theta[12], theta[13], theta[14], 1)
for repeat in range(depth):
for i, q in enumerate(which_qubits):
self.u3(theta[repeat][i][0], theta[repeat][i][1], theta[repeat][i][2], which_qubit=q)
for i in range(len(which_qubits) - 1):
self.cnot([which_qubits[i], which_qubits[i + 1]])
self.cnot([which_qubits[-1], which_qubits[0]])
def __add_real_block(self, theta, position):
r"""
......@@ -966,7 +1092,7 @@ class UAnsatz:
self.__add_complex_block(theta[int((i - position[0]) / 2)], [i, i + 1])
def real_block_layer(self, theta, depth):
r"""添加 ``depth`` 层包含Ry门和CNOT门的弱纠缠层。
r"""添加 ``depth`` 层包含 Ry 门和 CNOT 门的弱纠缠层。
Note:
这一层量子门的数学表示形式为实数酉矩阵。
......@@ -975,7 +1101,7 @@ class UAnsatz:
``theta`` 的维度为 ``(depth, n-1, 4)``
Args:
theta(Variable): Ry门的旋转角度
theta(Variable): Ry 门的旋转角度
depth(int): 纠缠层的深度
代码示例:
......@@ -1013,7 +1139,7 @@ class UAnsatz:
self.__add_real_layer(theta[i][int((self.n - 1) / 2):], [1, self.n - 1])
def complex_block_layer(self, theta, depth):
r"""添加 ``depth`` 层包含U3门和CNOT门的弱纠缠层。
r"""添加 ``depth`` 层包含 U3 门和 CNOT 门的弱纠缠层。
Note:
这一层量子门的数学表示形式为复数酉矩阵。
......@@ -1022,7 +1148,7 @@ class UAnsatz:
``theta`` 的维度为 ``(depth, n-1, 12)``
Args:
theta (Variable): U3门的角度信息
theta (Variable): U3 门的角度信息
depth (int): 纠缠层的深度
代码示例:
......@@ -1062,7 +1188,7 @@ class UAnsatz:
def __local_H_prob(cir, hamiltonian, shots=1024):
r"""
构造出Pauli测量电路并测量ancilla,处理实验结果来得到 ``H`` (只有一项)期望值的实验测量值。
构造出 Pauli 测量电路并测量 ancilla,处理实验结果来得到 ``H`` (只有一项)期望值的实验测量值。
Note:
这是内部函数,你并不需要直接调用到该函数。
......@@ -1105,12 +1231,12 @@ def __local_H_prob(cir, hamiltonian, shots=1024):
def H_prob(cir, H, shots=1024):
r"""构造Pauli测量电路并测量关于H的期望值。
r"""构造 Pauli 测量电路并测量关于 H 的期望值。
Args:
cir (UAnsatz): UAnsatz的一个实例化对象
cir (UAnsatz): UAnsatz 的一个实例化对象
H (list): 记录哈密顿量信息的列表
shots (int, optional): 默认为1024,表示测量次数;若为0,则表示返回测量期望值的精确值,即测量无穷次后的期望值
shots (int, optional): 默认为 1024,表示测量次数;若为 0,则表示返回测量期望值的精确值,即测量无穷次后的期望值
Returns:
float: 测量得到的H的期望值
......
# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy as np
import paddle.fluid as fluid
import paddle
from paddle_quantum.circuit import UAnsatz
from paddle_quantum.utils import NKron, partial_trace, dagger
from paddle.complex import matmul, trace, elementwise_div, kron, elementwise_add, elementwise_mul
from paddle.fluid.framework import ComplexVariable
from paddle.fluid.layers import diag, sin, cos, concat, zeros, ones
from paddle_quantum.state import isotropic_state, bell_state
from math import log2, sqrt
from numpy import pi as PI
class LoccStatus(object):
r"""用于表示 LOCCNet 中的一个 LOCC 态节点。
由于我们在 LOCC 中不仅关心量子态的解析形式,同时还关心得到它的概率,以及是经过怎样的测量而得到。因此该类包含三个成员变量:量子态 ``state`` 、得到这个态的概率 ``prob`` 和得到这个态的测量的测量结果是什么,即 ``measured_result`` 。
Attributes:
state (numpy.ndarray): 表示量子态的矩阵形式
prob (numpy.ndarray): 表示得到这个量子态的概率
measured_result (str): 表示得到这个态的测量函数的测量结果
"""
def __init__(self, state=None, prob=None, measured_result=None):
r"""构造函数,用于实例化一个 ``LoccStatus`` 对象。
Args:
state (numpy.ndarray): 默认为 ``None`` ,该 ``LoccStatus`` 的量子态的矩阵形式
prob (numpy.ndarray): 默认为 ``None`` ,得到该量子态的概率
measured_result (str): 默认为 ``None`` ,表示得到这个态的测量函数的测量结果
"""
super(LoccStatus, self).__init__()
if state is None and prob is None and measured_result is None:
self.state = fluid.dygraph.to_variable(np.array([1], dtype=np.complex128))
self.prob = fluid.dygraph.to_variable(np.array([1], dtype=np.float64))
self.measured_result = ""
else:
self.state = state
self.prob = prob
self.measured_result = measured_result
def clone(self):
r"""创建一个当前对象的副本。
Returns:
LoccStatus: 当前对象的副本
"""
return LoccStatus(self.state, self.prob, self.measured_result)
def __getitem__(self, item):
if item == 0:
return self.state
elif item == 1:
return self.prob
elif item == 2:
return self.measured_result
else:
raise ValueError("too many values to unpack (expected 3)")
def __repr__(self):
return f"state: {self.state.numpy()}\nprob: {self.prob.numpy()[0]}\nmeasured_result: {self.measured_result}"
def __str(self):
return f"state: {self.state.numpy()}\nprob: {self.prob.numpy()[0]}\nmeasured_result: {self.measured_result}"
class LoccParty(object):
r"""LOCC 的参与方。
Attributes:
qubit_number (int): 参与方的量子比特数量
"""
def __init__(self, qubit_number):
r"""
构造函数,用于实例化一个 ``LoccParty`` 对象。
Args:
qubit_number (int): 参与方的量子比特数量
"""
super(LoccParty, self).__init__()
self.qubit_number = qubit_number
self.qubits = [None] * qubit_number
def __setitem__(self, key, value):
self.qubits[key] = value
def __getitem__(self, item):
return self.qubits[item]
def __len__(self):
return self.qubit_number
class LoccAnsatz(UAnsatz):
r"""继承 ``UAnsatz`` 类,目的是建立在 LOCC 任务上的电路模板。
在 LOCC 任务中,每一方参与者只能在自己的量子比特上进行量子操作。因此我们只允许在每一方的量子比特上添加本地电路门。
Attributes:
party (LoccParty): 参与方
m (int): 参与方的量子比特数量
"""
def __init__(self, party, n):
r"""构造函数,用于实例化一个 ``LoccAnsatz`` 的对象。
Args:
party (LoccParty): 参与方
n (int): 全局的量子比特数量
"""
super(LoccAnsatz, self).__init__(n)
self.party = party
self.m = len(self.party)
self.measure = None
self.expecval = None
def run(self, status=None):
r"""运行当前添加的电路门,并获得运行后的 LOCC 态节点。
Args:
status (LoccStatus or list): 作为LOCC下的量子电路的输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或由其组成的 ``list``
Returns:
LoccStatus or list: 量子线路运行后得到的 LOCC 态节点,类型为 ``LoccStatus`` 或由其组成的 ``list``
"""
if isinstance(status, LoccStatus):
assert int(log2(sqrt(status.state.numpy().size))) == self.n, "the length of qubits should be same"
state = super(LoccAnsatz, self).run_density_matrix(status.state)
new_status = LoccStatus(state, status.prob, status.measured_result)
elif isinstance(status, list):
assert int(log2(sqrt(status[0].state.numpy().size))) == self.n, "the length of qubits should be same"
new_status = list()
for each_status in status:
state = super(LoccAnsatz, self).run_density_matrix(each_status.state)
new_status.append(LoccStatus(state, each_status.prob, each_status.measured_result))
else:
raise ValueError("can't recognize the input status")
return new_status
def rx(self, theta, which_qubit):
r"""添加关于 x 轴的单量子比特旋转门。
Args:
theta (Variable): 量子门的角度
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).rx(theta, which_qubit)
def ry(self, theta, which_qubit):
r"""添加关于 y 轴的单量子比特旋转门。
Args:
theta (Variable): 量子门的角度
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).ry(theta, which_qubit)
def rz(self, theta, which_qubit):
r"""添加关于 z 轴的单量子比特旋转门。
Args:
theta (Variable): 量子门的角度
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).rz(theta, which_qubit)
def cnot(self, control):
r"""添加一个 CNOT 门。
Args:
control (list): 作用在的 qubit 的编号,``control[0]`` 为控制位,``control[1]`` 为目标位,其值都应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
control = [self.party[which_qubit] for which_qubit in control]
super(LoccAnsatz, self).cnot(control)
def swap(self, control):
r"""添加一个 SWAP 门。
Args:
control (list): 作用在的 qubit 的编号,``control[0]`` 和``control[1]`` 是想要交换的位,其值都应该在 :math:`[0, m)`范围内, :math:`m` 为该参与方的量子比特数
"""
control = [self.party[which_qubit] for which_qubit in control]
super(LoccAnsatz, self).swap(control)
def x(self, which_qubit):
r"""添加单量子比特 X 门。
Args:
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).x(which_qubit)
def y(self, which_qubit):
r"""添加单量子比特 Y 门。
Args:
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).y(which_qubit)
def z(self, which_qubit):
r"""添加单量子比特 Z 门。
Args:
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).z(which_qubit)
def h(self, which_qubit):
r"""添加单量子比特 Hadamard 门。
Args:
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).h(which_qubit)
def s(self, which_qubit):
r"""添加单量子比特 S 门。
Args:
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).s(which_qubit)
def t(self, which_qubit):
r"""添加单量子比特 T 门。
Args:
which_qubit (int): 添加该门量子比特编号
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).z(which_qubit)
def u3(self, theta, phi, lam, which_qubit):
r"""添加一个单量子比特的旋转门。
Args:
theta (Variable): 旋转角度 :math:`\theta` 。
phi (Variable): 旋转角度 :math:`\phi` 。
lam (Variable): 旋转角度 :math:`\lambda` 。
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该量子线路的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).u3(theta, phi, lam, which_qubit)
def superposition_layer(self):
r"""添加一层 Hadamard 门。
"""
for which_qubit in self.party.qubits:
self.h(which_qubit)
def weak_superposition_layer(self):
r"""添加一层旋转角度为 :math:`\pi/4` 的 Ry 门。
"""
_theta = fluid.dygraph.to_variable(np.array([np.pi / 4]))
for which_qubit in self.party.qubits:
self.ry(_theta, which_qubit)
def real_entangled_layer(self, theta, depth):
r"""添加 ``depth`` 层包含 Ry 门和 CNOT 门的强纠缠层。
Note:
这一层量子门的数学表示形式为实数酉矩阵。
Attention:
``theta`` 的维度为 ``(depth, m, 1)``
Args:
theta (Variable): Ry 门的旋转角度
depth (int): 纠缠层的深度
which_qubits(list): 作用的量子比特编号
"""
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 1, 'the shape of theta is not right'
assert theta.shape[1] == len(self.party), 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
for repeat in range(depth):
for i in range(self.party.qubits):
self.ry(theta=theta[repeat][i][0], which_qubit=i)
for idx in range(0, self.m - 1):
self.cnot(control=[idx, idx + 1])
self.cnot([len(self.party) - 1, 0])
def complex_entangled_layer(self, theta, depth, which_qubits=None):
r"""添加 ``depth`` 层包含 U3 门和 CNOT 门的强纠缠层。
Note:
这一层量子门的数学表示形式为复数酉矩阵。
Attention:
``theta`` 的维度为 ``(depth, m, 3)``,最低维内容为对应的 ``u3`` 的参数 ``(theta, phi, lam)``
Args:
theta (Variable): U3 门的旋转角度
depth (int): 纠缠层的深度
which_qubits(list): 作用的量子比特编号
"""
if which_qubits is None:
which_qubits = list(range(0, len(self.party)))
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 3, 'the shape of theta is not right'
# assert theta.shape[1] == self.m, 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
for repeat in range(depth):
for i, q in enumerate(which_qubits):
self.u3(theta[repeat][i][0], theta[repeat][i][1], theta[repeat][i][2], q)
for i in range(self.m - 1):
self.cnot([which_qubits[i], which_qubits[i + 1]])
self.cnot([which_qubits[-1], which_qubits[0]])
def universal_2_qubit_gate(self, theta, which_qubits):
r"""添加 2-qubit 通用门,这个通用门需要 15 个参数。
Args:
theta (Variable): 2-qubit 通用门的参数,其维度为 ``(15, )``
which_qubits(list): 作用的量子比特编号
"""
super(LoccAnsatz, self).universal_2_qubit_gate(theta, which_qubits)
def universal_3_qubit_gate(self, theta, which_qubits):
r"""添加 3-qubit 通用门,这个通用门需要 81 个参数。
Note:
参考: https://cds.cern.ch/record/708846/files/0401178.pdf
Args:
theta (Variable): 3-qubit 通用门的参数,其维度为 ``(81, )``
which_qubits(list): 作用的量子比特编号
"""
super(LoccAnsatz, self).universal_3_qubit_gate(theta, which_qubits)
def real_block_layer(self, theta, depth):
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'The dimension of theta is not right'
_depth, _number, block = theta.shape
assert depth > 0, 'depth must be greater than zero'
assert _depth == depth, 'the depth of parameters has a mismatch'
assert _number == self.m - 1 and block == 4, 'The shape of theta is not right'
if self.m % 2 == 0:
for i in range(depth):
self.__add_real_layer(theta[i][:int(self.m / 2)], [0, self.m - 1])
self.__add_real_layer(theta[i][int(self.m / 2):], [1, self.m - 2]) if self.m > 2 else None
else:
for i in range(depth):
self.__add_real_layer(theta[i][:int((self.m - 1) / 2)], [0, self.m - 2])
self.__add_real_layer(theta[i][int((self.m - 1) / 2):], [1, self.m - 1])
def complex_block_layer(self, theta, depth):
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'The dimension of theta is not right'
assert depth > 0, 'depth must be greater than zero'
_depth, _number, block = theta.shape
assert _depth == depth, 'the depth of parameters has a mismatch'
assert _number == self.m - 1 and block == 12, 'The shape of theta is not right'
if self.m % 2 == 0:
for i in range(depth):
self.__add_complex_layer(theta[i][:int(self.m / 2)], [0, self.m - 1])
self.__add_complex_layer(theta[i][int(self.m / 2):], [1, self.m - 2]) if self.n > 2 else None
else:
for i in range(depth):
self.__add_complex_layer(theta[i][:int((self.m - 1) / 2)], [0, self.m - 2])
self.__add_complex_layer(theta[i][int((self.m - 1) / 2):], [1, self.m - 1])
def __add_real_block(self, theta, position):
r"""
Add a real block to the circuit in (position). theta is a one dimensional tensor
Note:
这是内部函数,你并不需要直接调用到该函数。
"""
assert len(theta) == 4, 'the length of theta is not right'
assert 0 <= position[0] < self.m and 0 <= position[1] < self.m, 'position is out of range'
self.ry(theta[0], position[0])
self.ry(theta[1], position[1])
self.cnot([position[0], position[1]])
self.ry(theta[2], position[0])
self.ry(theta[3], position[1])
def __add_complex_block(self, theta, position):
r"""
Add a complex block to the circuit in (position). theta is a one dimensional tensor
Note:
这是内部函数,你并不需要直接调用到该函数。
"""
assert len(theta) == 12, 'the length of theta is not right'
assert 0 <= position[0] < self.m and 0 <= position[1] < self.m, 'position is out of range'
self.u3(theta[0], theta[1], theta[2], position[0])
self.u3(theta[3], theta[4], theta[5], position[1])
self.cnot([position[0], position[1]])
self.u3(theta[6], theta[7], theta[8], position[0])
self.u3(theta[9], theta[10], theta[11], position[1])
def __add_real_layer(self, theta, position):
r"""
Add a real layer on the circuit. theta is a two dimensional tensor. position is the qubit range the layer needs to cover
Note:
这是内部函数,你并不需要直接调用到该函数。
"""
assert theta.shape[1] == 4 and theta.shape[0] == (position[1] - position[0] + 1) / 2, \
'the shape of theta is not right'
for i in range(position[0], position[1], 2):
self.__add_real_block(theta[int((i - position[0]) / 2)], [i, i + 1])
def __add_complex_layer(self, theta, position):
r"""
Add a complex layer on the circuit. theta is a two dimensional tensor. position is the qubit range the layer needs to cover
Note:
这是内部函数,你并不需要直接调用到该函数。
"""
assert theta.shape[1] == 12 and theta.shape[0] == (position[1] - position[0] + 1) / 2, \
'the shape of theta is not right'
for i in range(position[0], position[1], 2):
self.__add_complex_block(theta[int((i - position[0]) / 2)], [i, i + 1])
class LoccNet(fluid.dygraph.Layer):
r"""用于设计我们的 LOCC 下的 protocol,并进行验证或者训练。
"""
def __init__(self):
r"""构造函数,用于实例化一个 LoccNet 对象。
"""
super(LoccNet, self).__init__()
self.parties_by_number = list()
self.parties_by_name = dict()
self.init_status = LoccStatus()
def set_init_status(self, state, which_qubits):
r"""对 LoccNet 的初始 LOCC 态节点进行初始化。
Args:
state (ComplexVariable): 输入的量子态的矩阵形式
which_qubits (tuple or list): 该量子态所对应的量子比特,其形式为 ``(party_id, qubit_id)`` 的 ``tuple`` ,或者由其组成的 ``list``
"""
if isinstance(which_qubits, tuple):
which_qubits = [which_qubits]
temp_len = int(log2(sqrt(self.init_status.state.numpy().size)))
self.init_status.state = kron(self.init_status.state, state)
for idx, (party_id, qubit_id) in enumerate(which_qubits):
if isinstance(party_id, str):
self.parties_by_name[party_id][qubit_id] = (temp_len + idx)
elif isinstance(party_id, int):
self.parties_by_number[party_id][qubit_id] = (temp_len + idx)
else:
raise ValueError
def partial_state(self, status, which_qubits, is_desired=True):
r"""得到你想要的部分量子比特上的量子态。
Args:
status (LoccStatus or list): 输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
which_qubits (tuple or list): 指定的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple`` ,或者由其组成的 ``list``
is_desired (bool, optional): 默认是 ``True`` ,即返回量子比特上的部分量子态。如果为 ``False`` ,则抛弃这部分量子比特上的量子态,返回剩下的部分量子态
Returns:
LoccStatus or list: 得到部分量子态后的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
"""
if isinstance(which_qubits, tuple):
which_qubits = [which_qubits]
qubits_list = list()
for party_id, qubit_id in which_qubits:
if isinstance(party_id, str):
qubits_list.append(self.parties_by_name[party_id][qubit_id])
elif isinstance(party_id, int):
qubits_list.append(self.parties_by_number[party_id][qubit_id])
else:
raise ValueError
m = len(qubits_list)
if isinstance(status, LoccStatus):
n = int(log2(sqrt(status.state.numpy().size)))
elif isinstance(status, list):
n = int(log2(sqrt(status[0].state.numpy().size)))
else:
raise ValueError("can't recognize the input status")
assert max(qubits_list) <= n, "qubit index out of range"
qubit2idx = list(range(0, n))
idx2qubit = list(range(0, n))
cir = UAnsatz(n)
for i, ele in enumerate(qubits_list):
if qubit2idx[ele] != i:
# if qubit2idx[ele] is i, then swap([ele, i]) is identity
cir.swap([i, qubit2idx[ele]])
qubit2idx[idx2qubit[i]] = qubit2idx[ele]
idx2qubit[qubit2idx[ele]] = idx2qubit[i]
idx2qubit[i] = ele
qubit2idx[ele] = i
if isinstance(status, LoccStatus):
state = cir.run_density_matrix(status.state)
if is_desired:
state = partial_trace(state, 2 ** m, 2 ** (n - m), 2)
else:
state = partial_trace(state, 2 ** m, 2 ** (n - m), 1)
new_status = LoccStatus(state, status.prob, status.measured_result)
elif isinstance(status, list):
new_status = list()
for each_status in status:
state = cir.run_density_matrix(each_status.state)
if is_desired:
state = partial_trace(state, 2 ** m, 2 ** (n - m), 2)
else:
state = partial_trace(state, 2 ** m, 2 ** (n - m), 1)
new_status.append(LoccStatus(state, each_status.prob, each_status.measured_result))
else:
raise ValueError("can't recognize the input status")
return new_status
def reset_state(self, status, state, which_qubits):
r"""用于重置你想要的量子比特上的部分量子态。
Args:
status (LoccStatus or list): 输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
state (ComplexVariable): 输入的量子态的矩阵形式
which_qubits (tuple or list): 指定需要被重置的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple``,或者由其组成的 ``list``
Returns:
LoccStatus or list: 重置部分量子比特的量子态后的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
"""
if isinstance(which_qubits, tuple):
which_qubits = [which_qubits]
qubits_list = list()
for party_id, qubit_id in which_qubits:
if isinstance(party_id, str):
qubits_list.append(self.parties_by_name[party_id][qubit_id])
elif isinstance(party_id, int):
qubits_list.append(self.parties_by_number[party_id][qubit_id])
else:
raise ValueError
m = len(qubits_list)
if isinstance(status, LoccStatus):
n = int(log2(sqrt(status.state.numpy().size)))
elif isinstance(status, list):
n = int(log2(sqrt(status[0].state.numpy().size)))
else:
raise ValueError("can't recognize the input status")
assert max(qubits_list) <= n, "qubit index out of range"
qubit2idx = list(range(0, n))
idx2qubit = list(range(0, n))
swap_history = list()
cir0 = UAnsatz(n)
for i, ele in enumerate(qubits_list):
if qubit2idx[ele] != i: # if qubit2idx[ele] is i, then swap([ele, i]) is identity
swap_history.append((i, qubit2idx[ele]))
cir0.swap([i, qubit2idx[ele]])
qubit2idx[idx2qubit[i]] = qubit2idx[ele]
idx2qubit[qubit2idx[ele]] = idx2qubit[i]
idx2qubit[i] = ele
qubit2idx[ele] = i
cir1 = UAnsatz(n)
swap_cnt = len(swap_history)
for idx in range(0, swap_cnt):
a, b = swap_history[swap_cnt - 1 - idx]
cir1.swap([b, a])
if isinstance(status, LoccStatus):
_state = cir0.run_density_matrix(status.state)
_state = partial_trace(_state, 2 ** m, 2 ** (n - m), 1)
_state = kron(state, _state)
_state = cir1.run_density_matrix(_state)
new_status = LoccStatus(_state, status.prob, status.measured_result)
elif isinstance(status, list):
new_status = list()
for each_status in status:
_state = cir0.run_density_matrix(each_status.state)
_state = partial_trace(_state, 2 ** m, 2 ** (n - m), 1)
_state = kron(state, _state)
_state = cir1.run_density_matrix(_state)
new_status.append(LoccStatus(_state, each_status.prob, each_status.measured_result))
else:
raise ValueError("can't recognize the input status")
return new_status
def add_new_party(self, qubits_number, party_name=None):
r"""添加一个新的 LOCC 的参与方。
Args:
qubits_number (int): 参与方的量子比特个数
party_name (str, optional): 可选参数,默认为 ``None``,参与方的名字
Note:
你可以使用字符串或者数字对 party 进行索引。如果你想使用字符串索引,需要每次指定 ``party_name``;如果你想使用数字索引,则不需要指定 ``party_name``,其索引数字会从 0 开始依次增长。
Returns:
int or str: 该参与方的 ID,为数字或者字符串
"""
party_id = None
if party_name is None:
party_id = party_name
party_name = str(len(self.parties_by_name))
elif isinstance(party_name, str) is False:
raise ValueError
if party_id is None:
party_id = len(self.parties_by_name)
new_party = LoccParty(qubits_number)
self.parties_by_name[party_name] = new_party
self.parties_by_number.append(new_party)
return party_id
def create_ansatz(self, party_id):
r"""创建一个新的本地电路模板。
Args:
party_id (int or str): 参与方的 ID
Returns:
LoccAnsatz: 一个本地的量子电路
"""
if isinstance(party_id, int):
return LoccAnsatz(self.parties_by_number[party_id], self.get_qubit_number())
elif isinstance(party_id, str):
return LoccAnsatz(self.parties_by_name[party_id], self.get_qubit_number())
else:
raise ValueError
def __measure_parameterized(self, state, which_qubits, result_desired, theta):
r"""进行参数化的测量。
Args:
state (ComplexVariable): 输入的量子态
which_qubits (list): 测量作用的量子比特编号
result_desired (list): 期望得到的测量结果,如 ``"0"``、``"1"`` 或者 ``["0", "1"]``
theta (Variable): 测量运算的参数
Returns:
ComplexVariable: 测量坍塌后的量子态
Variable:测量坍塌得到的概率
list: 测量得到的结果(0 或 1)
"""
n = self.get_qubit_number()
assert len(which_qubits) == len(result_desired), \
"the length of qubits wanted to be measured and the result desired should be same"
op_list = [fluid.dygraph.to_variable(np.eye(2, dtype=np.complex128))] * n
for idx in range(0, len(which_qubits)):
i = which_qubits[idx]
ele = result_desired[idx]
if int(ele) == 0:
basis0 = fluid.dygraph.to_variable(np.array([[1, 0], [0, 0]], dtype=np.complex128))
basis1 = fluid.dygraph.to_variable(np.array([[0, 0], [0, 1]], dtype=np.complex128))
rho0 = elementwise_mul(basis0, cos(theta[idx]))
rho1 = elementwise_mul(basis1, sin(theta[idx]))
rho = elementwise_add(rho0, rho1)
op_list[i] = rho
elif int(ele) == 1:
# rho = diag(concat([cos(theta[idx]), sin(theta[idx])]))
# rho = ComplexVariable(rho, zeros((2, 2), dtype="float64"))
basis0 = fluid.dygraph.to_variable(np.array([[1, 0], [0, 0]], dtype=np.complex128))
basis1 = fluid.dygraph.to_variable(np.array([[0, 0], [0, 1]], dtype=np.complex128))
rho0 = elementwise_mul(basis0, sin(theta[idx]))
rho1 = elementwise_mul(basis1, cos(theta[idx]))
rho = elementwise_add(rho0, rho1)
op_list[i] = rho
else:
print("cannot recognize the results_desired.")
# rho = ComplexVariable(ones((2, 2), dtype="float64"), zeros((2, 2), dtype="float64"))
measure_operator = fluid.dygraph.to_variable(op_list[0])
if n > 1:
for idx in range(1, len(op_list)):
measure_operator = kron(measure_operator, op_list[idx])
state_measured = matmul(matmul(measure_operator, state), dagger(measure_operator))
prob = trace(matmul(matmul(dagger(measure_operator), measure_operator), state)).real
state_measured = elementwise_div(state_measured, prob)
return state_measured, prob, result_desired
def __measure_parameterless(self, state, which_qubits, result_desired):
r"""进行 01 测量。
Args:
state (ComplexVariable): 输入的量子态
which_qubits (list): 测量作用的量子比特编号
result_desired (list): 期望得到的测量结果,如 ``"0"``、``"1"`` 或者 ``["0", "1"]``
Returns:
ComplexVariable: 测量坍塌后的量子态
ComplexVariable:测量坍塌得到的概率
list: 测量得到的结果(0 或 1)
"""
n = self.get_qubit_number()
assert len(which_qubits) == len(result_desired), \
"the length of qubits wanted to be measured and the result desired should be same"
op_list = [np.eye(2, dtype=np.complex128)] * n
for i, ele in zip(which_qubits, result_desired):
k = int(ele)
rho = np.zeros((2, 2), dtype=np.complex128)
rho[int(k), int(k)] = 1
op_list[i] = rho
if n > 1:
measure_operator = fluid.dygraph.to_variable(NKron(*op_list))
else:
measure_operator = fluid.dygraph.to_variable(op_list[0])
state_measured = matmul(matmul(measure_operator, state), dagger(measure_operator))
prob = trace(matmul(matmul(dagger(measure_operator), measure_operator), state)).real
state_measured = elementwise_div(state_measured, prob)
return state_measured, prob, result_desired
def measure(self, status, which_qubits, results_desired, theta=None):
r"""对 LOCC 态节点进行 01 测量或者含参测量。
Args:
state (LoccStatus or list): 输入的量子态,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
which_qubits (tuple or list): 测量作用的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple`` ,或者由其组成的 ``list``
result_desired (str or list): 期望得到的测量结果,用字符串进行表示,其类型为 ``str`` 或者由 ``str`` 组成的 ``list``
theta (Variable): 测量运算的参数,默认是 ``None``,表示 01 测量;若要使用含参测量则需要赋值
Returns:
LoccStatus or list: 测量后得到的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
"""
if isinstance(which_qubits, tuple):
which_qubits = [which_qubits]
if isinstance(results_desired, str):
results_desired = [results_desired]
elif not isinstance(results_desired, list):
raise ValueError("cannot recognize the input of results_desired")
qubits_list = list()
for party_id, qubit_id in which_qubits:
if isinstance(party_id, int):
qubits_list.append((self.parties_by_number[party_id][qubit_id]))
elif isinstance(party_id, str):
qubits_list.append((self.parties_by_name[party_id][qubit_id]))
else:
raise ValueError
if isinstance(status, LoccStatus):
existing_result = status.measured_result
prior_prob = status.prob
new_status = list()
for result_desired in results_desired:
if theta is None:
result_measured = self.__measure_parameterless(status.state, qubits_list, result_desired)
else:
result_measured = self.__measure_parameterized(status.state, qubits_list, result_desired, theta)
state, prob, res = result_measured
new_status.append(LoccStatus(state, prior_prob * prob, existing_result + res))
if len(new_status) == 1:
new_status = new_status[0]
elif isinstance(status, list):
new_status = list()
for each_status in status:
existing_result = each_status.measured_result
prior_prob = each_status.prob
for result_desired in results_desired:
if theta is None:
result_measured = self.__measure_parameterless(each_status.state, qubits_list, result_desired)
else:
result_measured = self.__measure_parameterized(each_status.state, qubits_list, result_desired, theta)
state, prob, res = result_measured
new_status.append(LoccStatus(state, prior_prob * prob, existing_result + res))
else:
raise ValueError("can't recognize the input status")
return new_status
def get_qubit_number(self):
r"""得到该 LoccNet 的量子比特数量。
Returns:
int: 当前量子比特数量
"""
qubits_number = 0
for party in self.parties_by_number:
qubits_number += party.qubit_number
return qubits_number
# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This simulator uses statevector(Tensor) to simulate quantum behaviors.
Basically, the core of the algorithm is tensor contraction with one-way calculation that each gate is
......@@ -185,6 +199,16 @@ def cx_gate_matrix():
[0, 0, 0, 1],
[0, 0, 1, 0]], dtype=complex).reshape(2, 2, 2, 2)
def swap_gate_matrix():
"""
Control Not
:return:
"""
return np.array([[1, 0, 0, 0],
[0, 0, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 1]], dtype=complex).reshape(2, 2, 2, 2)
### PaddleE ###
def normalize_axis(axis, ndim):
......@@ -359,7 +383,7 @@ def StateTranfer(state, gate_name, bits, params=None):
"""
To transfer state by only gate name and bits
:param state: the last step state, can be init vector or the last step vector.
:param gate_name:x,y,z,h,CNOT
:param gate_name:x,y,z,h,CNOT, SWAP
:param bits: the gate working on the bits.
:param params: params for u gate.
:return: the updated state
......@@ -379,6 +403,9 @@ def StateTranfer(state, gate_name, bits, params=None):
elif gate_name == 'CNOT':
# print('----------', gate_name, bits, '----------')
gate_matrix = cx_gate_matrix()
elif gate_name == 'SWAP':
# print('----------', gate_name, bits, '----------')
gate_matrix = swap_gate_matrix()
elif gate_name == 'u':
# print('----------', gate_name, bits, '----------')
gate_matrix = u_gate_matrix(params)
......
......@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy
import numpy as np
from numpy import concatenate
from numpy import trace as np_trace
from numpy import matmul as np_matmul
......@@ -26,7 +26,12 @@ __all__ = [
"w_state",
"density_op",
"density_op_random",
"completely_mixed_computational"
"completely_mixed_computational",
"bell_state",
"bell_diagonal_state",
"R_state",
"S_state",
"isotropic_state"
]
......@@ -52,19 +57,19 @@ def vec(n):
[[1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]]
"""
assert n > 0, 'qubit number must be larger than 1'
state = concatenate(([[1.0]], np_zeros([1, 2**n- 1])), axis=1)
state = concatenate(([[1.0]], np_zeros([1, 2**n - 1])), axis=1)
return state.astype("complex128")
def vec_random(n, real_or_complex=2):
r"""随机生成一个量子态的numpy形式。
r"""随机生成一个量子态的 numpy 形式。
Args:
n (int): 量子比特数量
real_or_complex (int, optional): 默认为2,即生成复数组;若为1,则生成实数组
real_or_complex (int, optional): 默认为 2,即生成复数组;若为 1,则生成实数组
Returns:
numpy.ndarray: 一个形状为 ``(1, 2**n)`` 的numpy数组
numpy.ndarray: 一个形状为 ``(1, 2**n)`` 的 numpy 数组
"""
assert n > 0, 'qubit number must be larger than 1'
assert real_or_complex == 1 or real_or_complex == 2, 'real_or_complex must be 1 or 2'
......@@ -74,20 +79,20 @@ def vec_random(n, real_or_complex=2):
# complex
else:
psi = np_random.randn(1, 2**n) + 1j * np_random.randn(1, 2**n)
psi = psi/numpy.linalg.norm(psi)
psi = psi/np.linalg.norm(psi)
return psi.astype("complex128")
def w_state(n, coeff=None):
r"""生成一个W-state的numpy形式。
r"""生成一个 W-state 的 numpy 形式。
Args:
n (int): 量子比特数量
coeff (numpy.ndarray, optional): 默认为 ``None`` ,即生成平均概率幅(系数)
Returns:
numpy.ndarray: 一个形状为 ``(1, 2**n)`` 的numpy数组
numpy.ndarray: 一个形状为 ``(1, 2**n)`` 的 numpy 数组
代码示例:
......@@ -104,7 +109,7 @@ def w_state(n, coeff=None):
"""
assert n > 0, 'qubit number must be larger than 1'
c = coeff if coeff is not None else numpy.ones((1, 2**n))/numpy.sqrt(n)
c = coeff if coeff is not None else np.ones((1, 2**n))/np.sqrt(n)
assert c.shape[0] == 1 and c.shape[1] == 2**n, 'The dimension of coeff is not right'
state = np_zeros((1, 2**n))
......@@ -115,13 +120,13 @@ def w_state(n, coeff=None):
def density_op(n):
r"""生成密度矩阵 :math:`|00..0\rangle \langle00..0|` 的numpy形式。
r"""生成密度矩阵 :math:`|00..0\rangle \langle00..0|` 的 numpy 形式。
Args:
n (int): 量子比特数量
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的numpy数组
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
代码示例:
......@@ -147,15 +152,15 @@ def density_op(n):
def density_op_random(n, real_or_complex=2, rank=None):
r"""随机生成一个密度矩阵的numpy形式。
r"""随机生成一个密度矩阵的 numpy 形式。
Args:
n (int): 量子比特数量
real_or_complex (int, optional): 默认为2,即生成复数组,若为1,则生成实数组
real_or_complex (int, optional): 默认为 2,即生成复数组,若为 1,则生成实数组
rank (int, optional): 矩阵的秩,默认为 :math:`2^n` (当 ``rank`` 为 ``None`` 时)
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的numpy数组
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
"""
assert n > 0, 'qubit number must be positive'
rank = rank if rank is not None else 2**n
......@@ -174,13 +179,19 @@ def density_op_random(n, real_or_complex=2, rank=None):
def completely_mixed_computational(n):
r"""生成完全混合态的密度矩阵的numpy形式。
r"""生成完全混合态的密度矩阵的 numpy 形式。
其矩阵形式为:
.. math::
\frac{I}{2^n}
Args:
n (int): 量子比特数量
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的numpy数组
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
代码示例:
......@@ -201,3 +212,225 @@ def completely_mixed_computational(n):
rho = np_eye(2**n)/2**n
return rho.astype('complex128')
def bell_state(n):
r"""生成(推广)贝尔态的密度矩阵的 numpy 形式。
其数学表达形式为:
.. math::
|\Phi_{D}\rangle=\frac{1}{\sqrt{D}} \sum_{j=0}^{D-1}|j\rangle_{A}|j\rangle_{B}
Args:
n (int): 量子比特数量。必须为大于等于 2 的偶数
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
代码示例:
.. code-block:: python
from paddle_quantum.state import bell_state
state = bell_state(2)
print(state)
::
[[0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]
[0. +0.j 0. +0.j 0. +0.j 0. +0.j]
[0. +0.j 0. +0.j 0. +0.j 0. +0.j]
[0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]]
"""
assert n > 0, "Qubit number must be positive"
assert n % 2 == 0, "Qubit number must be even"
dim = 2**n
local_dim = 2**int(n/2)
coeff = 1 / local_dim
state = np.zeros((dim, dim))
for i in range(0, dim, local_dim + 1):
for j in range(0, dim, local_dim + 1):
state[i, j] = coeff
return state.astype("complex128")
def bell_diagonal_state(p1, p2, p3, p4):
r"""生成对角贝尔态的密度矩阵的 numpy 形式。
其数学表达形式为:
.. math::
p_{1}|\Phi^{+}\rangle\langle\Phi^{+}|+p_{2}| \Psi^{+}\rangle\langle\Psi^{+}|+p_{3}| \Phi^{-}\rangle\langle\Phi^{-}| +
p4|\Psi^{-}\rangle\langle\Psi^{-}|
Args:
p1 (float): 第一个分量。
p2 (float): 第二个分量。
p3 (float): 第三个分量。
p4 (float): 第四个分量。
Note:
四个参数构成了一个概率分布,因此它们是非负数,加起来必为 1。
Returns:
numpy.ndarray: 一个形状为 ``(4, 4)`` 的 numpy 数组
代码示例:
.. code-block:: python
from paddle_quantum.state import bell_diagonal_state
state = bell_diagonal_state(0.25, 0.25, 0.25, 0.25)
print(state)
::
[[0.25+0.j 0. +0.j 0. +0.j 0. +0.j]
[0. +0.j 0.25+0.j 0. +0.j 0. +0.j]
[0. +0.j 0. +0.j 0.25+0.j 0. +0.j]
[0. +0.j 0. +0.j 0. +0.j 0.25+0.j]]
"""
# p4 = 1 - p1 - p2 - p3
# assert 0 <= p1 <= 1 and 0 <= p2 <= 1 and 0 <= p3 <= 1 and 0 <= p4 <= 1, "Probability must be in [0, 1]"
coeff = np.sqrt(0.5)
phi_p_vec = np.array([[coeff, 0, 0, coeff]])
phi_p_mat = np.matmul(phi_p_vec.T, phi_p_vec)
phi_m_vec = np.array([[coeff, 0, 0, -coeff]])
phi_m_mat = np.matmul(phi_m_vec.T, phi_m_vec)
psi_p_vec = np.array([[0, coeff, coeff, 0]])
psi_p_mat = np.matmul(psi_p_vec.T, psi_p_vec)
psi_m_vec = np.array([[0, coeff, -coeff, 0]])
psi_m_mat = np.matmul(psi_m_vec.T, psi_m_vec)
state = p1*phi_p_mat + p2*psi_p_mat + p3*phi_m_mat + p4*psi_m_mat
return state.astype("complex128")
def R_state(p):
r"""生成 R-state 的密度矩阵的 numpy 形式。
其数学表达形式为:
.. math::
p|\Psi^{+}\rangle\langle\Psi^{+}| + (1 - p)|11\rangle\langle11|
Args:
p (float): 控制生成 R-state 的参数。属于 :math:`[0, 1]` 区间内
Returns:
numpy.ndarray: 一个形状为 ``(4, 4)`` 的 numpy 数组
代码示例:
.. code-block:: python
from paddle_quantum.state import R_state
state = R_state(0.5)
print(state)
::
[[0. +0.j 0. +0.j 0. +0.j 0. +0.j]
[0. +0.j 0.25+0.j 0.25+0.j 0. +0.j]
[0. +0.j 0.25+0.j 0.25+0.j 0. +0.j]
[0. +0.j 0. +0.j 0. +0.j 0.5 +0.j]]
"""
assert 0 <= p <= 1, "Probability must be in [0, 1]"
coeff = np.sqrt(0.5)
psi_p_vec = np.array([[0, coeff, coeff, 0]])
psi_p_mat = np.matmul(psi_p_vec.T, psi_p_vec)
state_11 = np.zeros((4, 4))
state_11[3, 3] = 1
state = p*psi_p_mat + (1-p)*state_11
return state.astype("complex128")
def S_state(p):
r"""生成 S-state 的密度矩阵的 numpy 形式。
其数学表达形式为:
.. math::
p|\Phi^{+}\rangle\langle\Phi^{+}| + (1 - p)|00\rangle\langle00|
Args:
p (float): 控制生成 S-state 的参数。属于 :math:`[0, 1]` 区间内
Returns:
numpy.ndarray: 一个形状为 ``(4, 4)`` 的 numpy 数组
代码示例:
.. code-block:: python
from paddle_quantum.state import S_state
state = S_state(0.5)
print(state)
::
[[0.75+0.j 0. +0.j 0. +0.j 0.25+0.j]
[0. +0.j 0. +0.j 0. +0.j 0. +0.j]
[0. +0.j 0. +0.j 0. +0.j 0. +0.j]
[0.25+0.j 0. +0.j 0. +0.j 0.25+0.j]]
"""
assert 0 <= p <= 1, "Probability must be in [0, 1]"
phi_p = bell_state(2)
psi0 = np.zeros_like(phi_p)
psi0[0, 0] = 1
state = p * phi_p + (1-p) * psi0
return state.astype("complex128")
def isotropic_state(n, p):
r"""生成 isotropic state 的密度矩阵的 numpy 形式。
其数学表达形式为:
.. math::
p(\frac{1}{\sqrt{D}} \sum_{j=0}^{D-1}|j\rangle_{A}|j\rangle_{B}) + (1 - p)\frac{I}{2^n}
Args:
n (int): 量子比特数量。
p (float): 控制生成 isotropic state 的参数。属于 :math:`[0, 1]` 区间内
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
代码示例:
.. code-block:: python
from paddle_quantum.state import isotropic_state
state = isotropic_state(2, 0.5)
print(state)
::
[[0.375+0.j 0. +0.j 0. +0.j 0.25 +0.j]
[0. +0.j 0.125+0.j 0. +0.j 0. +0.j]
[0. +0.j 0. +0.j 0.125+0.j 0. +0.j]
[0.25 +0.j 0. +0.j 0. +0.j 0.375+0.j]]
"""
assert 0 <= p <= 1, "Probability must be in [0, 1]"
dim = 2**n
state = p*bell_state(n) + (1-p)*np.eye(dim)/dim
return state
# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
......@@ -48,7 +48,12 @@ __all__ = [
"NKron",
"dagger",
"random_pauli_str_generator",
"pauli_str_to_matrix"
"pauli_str_to_matrix",
"partial_transpose_2",
"partial_transpose",
"negativity",
"logarithmic_negativity",
"is_ppt"
]
......@@ -266,12 +271,12 @@ def random_pauli_str_generator(n, terms=3):
"""
pauli_str = []
for sublen in np_random.randint(1, high=n+1, size=terms):
# Tips: -1 <= coef < 1
coef = np_random.rand()*2-1
# Tips: -1 <= coeff < 1
coeff = np_random.rand()*2-1
ops = np_random.choice(['x', 'y', 'z'], size=sublen)
pos = np_random.choice(range(n), size=sublen, replace=False)
op_list = [ops[i]+str(pos[i]) for i in range(sublen)]
pauli_str.append([coef, ','.join(op_list)])
pauli_str.append([coeff, ','.join(op_list)])
return pauli_str
......@@ -315,3 +320,154 @@ def pauli_str_to_matrix(pauli_str, n):
matrices.append(coeff * NKron(sub_matrices[0], sub_matrices[1], *sub_matrices[2:]))
return sum(matrices)
def partial_transpose_2(density_op, sub_system=None):
r"""计算输入量子态的 partial transpose :math:`\rho^{T_A}`
Args:
density_op (numpy.ndarray): 量子态的密度矩阵形式
sub_system (int): 1或2,表示关于哪个子系统进行 partial transpose,默认为第二个
Returns:
float: 输入的量子态的 partial transpose
代码示例:
.. code-block:: python
from paddle_quantum.utils import partial_transpose_2
rho_test = np.arange(1,17).reshape(4,4)
partial_transpose_2(rho_test, sub_system=1)
::
[[ 1, 2, 9, 10],
[ 5, 6, 13, 14],
[ 3, 4, 11, 12],
[ 7, 8, 15, 16]]
"""
sys_idx = 2 if sub_system is None else 1
# Copy the density matrix and not corrupt the original one
transposed_density_op = numpy.copy(density_op)
if sys_idx == 2:
for j in [0, 2]:
for i in [0, 2]:
transposed_density_op[i:i+2, j:j+2] = density_op[i:i+2, j:j+2].transpose()
else:
transposed_density_op[2:4, 0:2] = density_op[0:2, 2:4]
transposed_density_op[0:2, 2:4] = density_op[2:4, 0:2]
return transposed_density_op
def partial_transpose(density_op, n):
r"""计算输入量子态的 partial transpose :math:`\rho^{T_A}`。
Args:
density_op (numpy.ndarray): 量子态的密度矩阵形式
Returns:
float: 输入的量子态的 partial transpose
"""
# Copy the density matrix and not corrupt the original one
transposed_density_op = numpy.copy(density_op)
for j in range(0, 2**n, 2):
for i in range(0, 2**n, 2):
transposed_density_op[i:i+2, j:j+2] = density_op[i:i+2, j:j+2].transpose()
return transposed_density_op
def negativity(density_op):
r"""计算输入量子态的 Negativity :math:`N = ||\frac{\rho^{T_A}-1}{2}||`。
Args:
density_op (numpy.ndarray): 量子态的密度矩阵形式
Returns:
float: 输入的量子态的 Negativity
代码示例:
.. code-block:: python
from paddle_quantum.utils import negativity
from paddle_quantum.state import bell_state
rho = bell_state(2)
print("Negativity of the Bell state is:", negativity(rho))
::
Negativity of the Bell state is: 0.5
"""
# Implement the partial transpose
density_op_T = partial_transpose_2(density_op)
# Calculate through the equivalent expression N = sum(abs(\lambda_i)) when \lambda_i<0
n = 0
eigen_val, _ = numpy.linalg.eig(density_op_T)
for val in eigen_val:
if val < 0:
n = n + numpy.abs(val)
return n
def logarithmic_negativity(density_op):
r"""计算输入量子态的 Logarithmic Negativity :math:`E_N = ||\rho^{T_A}||`。
Args:
density_op (numpy.ndarray): 量子态的密度矩阵形式
Returns:
float: 输入的量子态的 Logarithmic Negativity
代码示例:
.. code-block:: python
from paddle_quantum.utils import logarithmic_negativity
from paddle_quantum.state import bell_state
rho = bell_state(2)
print("Logarithmic negativity of the Bell state is:", logarithmic_negativity(rho))
::
Logarithmic negativity of the Bell state is: 1.0
"""
# Calculate the negativity
n = negativity(density_op)
# Calculate through the equivalent expression
log2_n = numpy.log2(2*n + 1)
return log2_n
def is_ppt(density_op):
r"""计算输入量子态是否满足 PPT 条件。
Args:
density_op (numpy.ndarray): 量子态的密度矩阵形式
Returns:
bool: 输入的量子态是否满足 PPT 条件
代码示例:
.. code-block:: python
from paddle_quantum.utils import is_ppt
from paddle_quantum.state import bell_state
rho = bell_state(2)
print("Whether the Bell state satisfies PPT condition:", is_ppt(rho))
::
Whether the Bell state satisfies PPT condition: False
"""
# By default the PPT condition is satisfied
ppt = True
# Detect negative eigenvalues from the partial transposed density_op
if negativity(density_op) > 0:
ppt = False
return ppt
\ No newline at end of file
# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
......@@ -23,7 +23,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup(
name='paddle-quantum',
version='1.1.1',
version='1.2.0',
author='Institute for Quantum Computing, Baidu INC.',
author_email='quantum@baidu.com',
description='Paddle Quantum is a quantum machine learning (QML) toolkit developed based on Baidu PaddlePaddle.',
......
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 纠缠蒸馏 -- BBPSSW 协议\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概述\n",
"\n",
"量子纠缠在量子通信,量子计算和其他许多量子技术中起着至关重要的作用。因此,如果我们想在这些领域构建实际应用,探测、传输和分发量子纠缠是必不可少的任务。但是在实际操作中,误差是不可避免的。这些误差可能来自于生产纠缠设备的缺陷,也可能是传输量子纠缠时的信道噪声。随着传输距离的增加,噪声会导致纠缠资源的不断减少。而纠缠蒸馏(entanglement distillation)这项技术的开发目的正是为了补偿各种噪声引起的纠缠资源消耗。基本的实现原理为通过操作多对含噪的纠缠态将纠缠资源集中在其中一对纠缠态上,并使之尽可能的接近**最大纠缠态**(qubit 情况下,通常是大家熟知的贝尔态)。从这个意义上讲,也可以将纠缠蒸馏看作一种提纯/纠错协议。此过程通常由 Alice 和 Bob 两个远程参与方执行,由于双方在空间上会相隔一定的距离,因此仅允许本地操作和经典通信(LOCC)[1]。纠缠蒸馏的概念最早由 C. H. Bennett 等人于 1996 年提出 [2],最初的协议按照作者们的首字母简写为 **BBPSSW 协议**。本教程主要介绍纠缠蒸馏的基本概念,具体的 BBPSSW 协议流程,以及如何通过量桨模拟该协议。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 贝尔态\n",
"\n",
"量子信息处理中的诸多协议都使用了贝尔态,比如量子隐形传态 [3],超密编码 [4] 和基于贝尔定理的量子密码学 [5]。如此广阔的应用范围使四个贝尔态成为尤其重要的研究课题。它们通常用定义为:\n",
"\n",
"$$\n",
"\\begin{align*}\n",
"|\\Phi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|0\\rangle_B \\pm |1\\rangle_A\\otimes|1\\rangle_B), \\\\\n",
"|\\Psi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|1\\rangle_B \\pm |1\\rangle_A\\otimes|0\\rangle_B). \n",
"\\tag{1}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"其中 $A$(Alice)持有一个量子比特,而 $B$(Bob)持有另一个量子比特。注意到这些纯态不能分解为两个纯态的张量积 $| \\varphi \\rangle_A \\otimes | \\psi \\rangle_B$。 **注释:这是纯态表示下纠缠态的重要特征。密度矩阵的表示下关于量子纠缠的定性条件会更为复杂,这里我们不做进一步讨论。**\n",
"\n",
"贝尔态还有一个有趣的特点,如果对其中一个子系统 $A$ 求偏迹,那么剩下子系统 $B$ 的密度矩阵就会变成 $I / 2$。举个例子,让我们一起观察贝尔态 $| \\Phi ^ {+} \\rangle_ {AB}$\n",
"\n",
"$$\n",
"\\begin{align*}\n",
"\\rho_B &\\equiv \\text{tr}_A(\\Phi^{+}_{AB}), \\\\\n",
"& = \\text{tr}_A(|\\Phi^{+}\\rangle\\langle\\Phi^+|_{AB}), \\\\\n",
"& = \\frac{1}{2}\\text{tr}_A\\big(|00\\rangle\\langle00|+|00\\rangle\\langle11|+|11\\rangle\\langle00|+|11\\rangle\\langle11|\\big),\\\\\n",
"& = \\frac{1}{2} \\big(\\text{tr}_A(|0\\rangle\\langle0|_A)\\cdot|0\\rangle\\langle0|_B + \\text{tr}_A(|0\\rangle\\langle1|_A)\\cdot|0\\rangle\\langle1|_B \\\\\n",
"& \\quad\\quad + \\text{tr}_A(|1\\rangle\\langle0|_A) \\cdot|1\\rangle\\langle0|_B+\n",
"\\text{tr}_A(|1\\rangle\\langle1|_A)\\cdot|1\\rangle\\langle1|_B \\big), \\\\\n",
"& = \\frac{1}{2} (|0\\rangle\\langle0|_B + |1\\rangle\\langle1|_B) = \\frac{1}{2} I.\n",
"\\tag{2}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"**注释:** 这是因为纠缠的量子比特对之间存在非经典的相关性,我们无法使用有关子系统的部分信息来描述整个系统。\n",
"\n",
"在量桨中,我们可以通过调用函数 `bell_state()` 便捷创建贝尔态 $ \\Phi ^ {+} _ {AB} = |\\Phi^{+}\\rangle\\langle\\Phi^+|_{AB}$(密度矩阵表示)。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:13.334418Z",
"start_time": "2021-01-27T06:13:12.936569Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"生成的量子态 Phi = \n",
" [[0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]\n",
" [0. +0.j 0. +0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0. +0.j 0. +0.j]\n",
" [0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]]\n"
]
}
],
"source": [
"from paddle_quantum.state import bell_state\n",
"N = 2 # 指定量子比特数量\n",
"rho = bell_state(N)\n",
"print(\"生成的量子态 Phi = \\n\", rho)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 量化纠缠资源\n",
"\n",
"在定性地了解了量子纠缠之后,我们希望能将读者对纠缠的理解提升到一个量化的水平。 首先,我们需要指出上述量子通信协议的有效性,比如量子隐形传态和超密编码,都取决于**纠缠资源的量**,或者说有多少纠缠资源可以被用作为 \"燃料\"去消耗。按照量子信息学界的惯例,**Negativity** $ \\mathcal {N}(\\rho)$ 和 **Logarithmic Negativity** $ E_{N}(\\rho)$ 是公认的可用于量化纠缠资源的指标 [6]。具体的定义如下,\n",
"\n",
"$$\n",
"\\mathcal{N}(\\rho) \\equiv \\frac{||\\rho^{T_B}||_1-1}{2},\n",
"\\tag{3}\n",
"$$\n",
"\n",
"以及\n",
"\n",
"$$\n",
"E_{N}(\\rho) \\equiv \\text{log}_2 ||\\rho^{T_B}||_1,\n",
"\\tag{4}\n",
"$$\n",
"\n",
"其中 $ T_B $ 代表相对于子系统 $ B $ 的部分转置 (partial transpose)操作,可以定义为 [7],\n",
"\n",
"$$\n",
"\\rho_{A B}^{T_{B}}:=(I \\otimes T)(\\rho_{A B})=\\sum_{i, j}(I_{A} \\otimes|i\\rangle\\langle j|_{B}) \\rho_{A B}(I_{A} \\otimes|i\\rangle\\langle j|_{B}).\n",
"\\tag{5}\n",
"$$\n",
"\n",
"\n",
"$||\\cdot ||_ 1$ 表示迹范数,$\\{|i\\rangle_B\\}$ 是 $B$ 系统上的一组基。上述两个指标间的联系为 $E_{N}(\\rho) = \\text{log}_2 (2\\mathcal{N}(\\rho)+1)$。 它们都与 PPT 的可分离性指标有关。\n",
"> *如果密度矩阵进行部分转置后没有负特征值(即半正定的),则称其具有 positive partial transpose, PPT。 否则,称为 negative partial transpose, NPT。*\n",
"\n",
"\n",
"我们引入此类度量标准的原因不仅是希望帮助更好地理解量子纠缠,更进一步希望这些指标可以指导我们进行蒸馏协议的研发。**注释:所有 PPT 量子态一般被认为处于弱纠缠,是不可蒸馏的**。 这意味着满足 PPT 条件的量子态都无法作为输入态来进行纠缠蒸馏。因此,在执行蒸馏方案之前,检查初始量子态是否满足该条件是非常必要的。以上提及的这些概念都通过调用量桨中的函数 `negativity()`,`logarithmic_negativity()`, `partial_transpose()`,还有 `is_ppt()`。\n",
"\n",
"**注释:** 更多关于定性认识量子纠缠的讨论,请参考这篇综述文章[8]。\n",
"\n",
"在介绍了许多新概念之后,我们提供了一个具体示例。对于特定的贝尔态 $|\\Phi^{+}\\rangle_{AB}$,我们有\n",
"\n",
"$$\n",
"\\begin{align*}\n",
"\\rho^{T_{B}} &= \\frac{1}{2}\n",
"\\big(|00\\rangle\\langle00|+|01\\rangle\\langle10|+|10\\rangle\\langle01|+|11\\rangle\\langle11|\\big),\\\\\n",
"& = \\frac{1}{2}\n",
"\\begin{bmatrix}\n",
"1 & 0 & 0 & 0\\\\\n",
"0 & 0 & 1 & 0\\\\\n",
"0 & 1 & 0 & 0\\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{bmatrix}\n",
".\n",
"\\tag{6}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"其含有负的特征值 $\\lambda_{-}(\\rho^{T_{B}}) = -1/2$ 并可以算出 $E_{N}(\\rho) = 1$。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:17.858393Z",
"start_time": "2021-01-27T06:13:13.360104Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"部分转置后的量子态为\n",
" [[0.5+0.j 0. +0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0.5+0.j 0. +0.j]\n",
" [0. +0.j 0.5+0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0. +0.j 0.5+0.j]]\n",
"With negativity = 0.5 and logarithmic_negativity = 1.0\n",
"输入态满足 PPT 条件因此无法被蒸馏? False\n"
]
}
],
"source": [
"from paddle_quantum.state import bell_state\n",
"from paddle_quantum.utils import negativity, logarithmic_negativity, partial_transpose, is_ppt\n",
"\n",
"input_state = bell_state(2)\n",
"transposed_state = partial_transpose(input_state, 2) # 2 代表量子比特数量\n",
"print(\"部分转置后的量子态为\\n\", transposed_state)\n",
"\n",
"neg = negativity(input_state)\n",
"log_neg = logarithmic_negativity(input_state)\n",
"print(\"With negativity =\", neg,\"and logarithmic_negativity =\", log_neg)\n",
"print(\"输入态满足 PPT 条件因此无法被蒸馏?\", is_ppt(input_state))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在纠缠蒸馏方案中,我们通常使用蒸馏后的输出态 $\\rho_{out}$ 和贝尔态 $|\\Phi^+\\rangle$ 之间的**保真度 (state fidelity)** $ F $ 来量化蒸馏方案的性能,其中\n",
"\n",
"$$\n",
"F(\\rho_{out}, \\Phi^+) \\equiv \\langle \\Phi^+|\\rho_{out}|\\Phi^+\\rangle.\n",
"\\tag{7}\n",
"$$\n",
"\n",
"之前提到过蒸馏任务中的目标是获得高保真度的量子态 $\\rho_{out}$,所以选择保真度作为度量标准来量化当前量子态和目标态之间的距离是合理的。我们可以调用量桨中函数 `state_fidelity(rho, sigma)` 来计算保真度,\n",
"\n",
"$$\n",
"F_s(\\rho,\\sigma) \\equiv \\text{tr}\\big( \\sqrt{\\sqrt{\\rho}\\sigma \\sqrt{\\rho}} \\big).\n",
"\\tag{8}\n",
"$$\n",
"\n",
"**注释:** 这两个保真度的公式的关系为 $F=F_s^2$ 。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:17.894726Z",
"start_time": "2021-01-27T06:13:17.865157Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"两个态的保真度是:\n",
"F= 0.7750000000000004\n"
]
}
],
"source": [
"from paddle_quantum.state import bell_state, isotropic_state\n",
"from paddle_quantum.utils import state_fidelity\n",
"\n",
"F = state_fidelity(bell_state(2), isotropic_state(2, 0.7))\n",
"print(\"两个态的保真度是:\\nF=\", F**2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"经过以上准备,我们可以开始介绍 BBPSSW 协议。\n",
"\n",
"\n",
"## BBPSSW 协议\n",
"\n",
"BBPSSW 协议将两份相同的两方量子态 $\\rho_{in}$ 蒸馏成输出态 $\\rho_{out}$,一份具有更高的保真度 $ F $(更接近贝尔态 $|\\Phi^+\\rangle$)的两方量子态。值得注意的是,BBPSSW 主要用于蒸馏 **Isotropic 态**(也称为 Werner 态),由 $|\\Phi^+\\rangle$ 和完全混合态(白噪声)$I/4$ 组成。\n",
"\n",
"$$\n",
"\\rho_{\\text{iso}}(p) = p\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + (1-p)\\frac{I}{4}, \\quad p \\in [0,1]\n",
"\\tag{9}\n",
"$$\n",
"\n",
"此量子态在 $p \\leq 1/3$ 时满足 PPT 条件,因此不可蒸馏。\n",
"\n",
"BBPSSW 蒸馏协议由两个远程参与方 $ A $(Alice)和 $ B $(Bob)执行。它们共享两份量子对 $ \\rho_{A_0B_0} $ 和 $ \\rho_{A_1B_1} $。每对的初始态均为 $ \\rho_ {in} = \\rho _ {\\text {iso}}(p)$。Alice 拥有两个量子比特 $ A_0,A_1 $,Bob 也拥有两个量子比特 $ B_0,B_1 $。通过这些初始设置,Alice 和 Bob 需要实现以下流程:\n",
"\n",
"1. Alice 和 Bob 先选择任意一对量子比特,**这对量子比特将作为最终的输出比特**。这里假设他们选择了 $A_0$ 和 $B_0$。\n",
"2. Alice 和 Bob 对他们手中的量子比特施加受控非门 (CNOT 门)。这里, $A_0$ 和 $B_0$ 作为控制比特,$A_1$ 和 $B_1$ 作为目标比特。\n",
"3. 接下来双方分别对 $A_1$ 和 $B_1$ 进行测量,并通过**经典通讯**来交换他们的测量结果 $m_{A_1}, m_{B_1}$。\n",
"4. 如果 Alice 和 Bob 的结果一致(00或11),那么他们可以宣布本次蒸馏过程成功,同时将 $A_0$ 和 $B_0$ 作为输出比特,输出态为 $\\rho_{out}$。相反,如果他们的测量结果不一致(01或10),那么本次蒸馏过程失败,丢弃量子比特 $A_0$ 和 $B_0$。无论蒸馏成功与否,测量之后的量子比特 $A_1,B_1$ 都不会再被使用。\n",
"\n",
"\n",
"<center><img src=\"figures/distillation-fig-BBPSSW.png\" height=\"200\" width=\"400\"></center>\n",
"<div style=\"text-align:center\">图 1: BBPSSW 纠缠蒸馏协议的示意图 </div>\n",
"\n",
"\n",
"**注释:** 纠缠蒸馏方案是概率性的,并非每次都能成功。因此,量化蒸馏方案性能的另一个指标是**成功概率$ p_ {succ} $**。实际的蒸馏协议总是在输出态的保真度 $F' = F(\\rho_{out},\\Phi^+)$ 和 $p_{succ}$ 之间进行权衡的。 从理论上讲,BBPSSW 协议的这两个特定指标公式如下:\n",
"$$\n",
"F^{\\prime}=\\frac{F^2 + \\frac{1}{9}(1-F)^2}{F^2 + \\frac{2}{3}F(1-F) + \\frac{5}{9}(1-F)^2},\n",
"\\tag{10}\n",
"$$\n",
"\n",
"初始态的保真度为 $F(\\rho_{in},\\Phi^+) = (1+3p)/4$。成功的概率为,\n",
"\n",
"$$\n",
"p_{\\text {succ }}=F^{2} + \\frac{2}{3}F(1-F) + \\frac{5}{9}(1-F)^2.\n",
"\\tag{11}\n",
"$$\n",
"\n",
"在下一节中,我们将介绍如何使用量桨模拟 BBPSSW 协议。\n",
"\n",
"**注释:** 如果输入态不是 Isotropic 态 ,则可以通过概率性的旋转操作将其去极化(depolarize)为 Isotropic 态。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Paddle Quantum 代码实现\n",
"\n",
"首先,我们需要导入所有依赖包:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:18.591447Z",
"start_time": "2021-01-27T06:13:17.898635Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.state import bell_state, isotropic_state\n",
"from paddle_quantum.utils import logarithmic_negativity, is_ppt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"为了方便起见,我们在此处计算 BBPSSW 的度量,然后与模拟结果进行比较。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:18.602671Z",
"start_time": "2021-01-27T06:13:18.593932Z"
}
},
"outputs": [],
"source": [
"def BBPSSW_metrics(p):\n",
" \"\"\"\n",
" 返回输出态的保真度以及 BBPSSW 协议的成功率。\n",
" \"\"\"\n",
" F_in = (1+3*p)/4\n",
" p_succ = F_in**2 + 2*F_in*(1-F_in)/3+ 5*(1-F_in)**2/9\n",
" F_out = (F_in**2 + (1-F_in)**2/9)/p_succ\n",
" \n",
" return F_in, F_out, p_succ"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在我们的示例中,输入态是 Isotropic 态,其中 $ p = 0.7 $,也就是说\n",
"\n",
"$$\n",
"\\rho_{in} = \\rho_{\\text{iso}}(0.7)= 0.7\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + 0.075 I.\n",
"\\tag{12}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:18.656842Z",
"start_time": "2021-01-27T06:13:18.627418Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"输入态的保真度是: 0.7749999999999999\n",
"输出态的保真度是: 0.8137583892617447\n",
"成功率是: 0.745\n",
"输入态满足 PPT 条件因此无法被蒸馏? False\n"
]
}
],
"source": [
"p = 0.7\n",
"F_in, F_out, p_succ = BBPSSW_metrics(p)\n",
"print(\"输入态的保真度是:\", F_in)\n",
"print(\"输出态的保真度是:\", F_out)\n",
"print(\"成功率是:\", p_succ)\n",
"print(\"输入态满足 PPT 条件因此无法被蒸馏?\", is_ppt(isotropic_state(2, p)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"然后,我们模拟 BBPSSW 协议,并检查结果是否与理论相符。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:18.726120Z",
"start_time": "2021-01-27T06:13:18.691784Z"
}
},
"outputs": [],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # 添加第一个参与方 Alice\n",
" # 第一个参数 2 代表着 Alice 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(2, party_name='Alice')\n",
" \n",
" # 添加第二个参与方 Bob\n",
" # 第一个参数 2 代表着 Bob 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(2, party_name='Bob')\n",
" \n",
" # 定义输入量子态 rho_in\n",
" _state = fluid.dygraph.to_variable(isotropic_state(2, p))\n",
" \n",
" # ('Alice', 0) 代表 Alice 的第一个量子比特 A0\n",
" # ('Bob', 0) 代表 Bob 的第一个量子比特 B0\n",
" self.set_init_status(_state, [('Alice', 0), ('Bob', 0)]) \n",
" \n",
" # ('Alice', 1) 代表 Alice 的第二个量子比特 A1\n",
" # ('Bob', 1) 代表 Bob 的第二个量子比特 B1\n",
" self.set_init_status(_state, [('Alice', 1), ('Bob', 1)]) \n",
" \n",
" def BBPSSW(self):\n",
" status = self.init_status\n",
" \n",
" # 创建 Alice 的局部操作\n",
" cir1 = self.create_ansatz('Alice')\n",
" cir1.cnot([0, 1])\n",
"\n",
" # 创建 Bob 的局部操作\n",
" cir2 = self.create_ansatz('Bob')\n",
" cir2.cnot([0, 1])\n",
" \n",
" # 运行电路\n",
" status = cir1.run(status)\n",
" status_mid = cir2.run(status)\n",
" \n",
" # ('Alice', 1) 代表测量 Alice 的第二个量子比特 A1\n",
" # ('Bob', 1) 代表测量 Bob 的第二个量子比特 B1\n",
" # ['00','11'] 代表着成功的蒸馏实验所需条件\n",
" # Alice 和 Bob 同时测得 '00' or '11'\n",
" status_mid = self.measure(status_mid, [('Alice', 1), ('Bob', 1)], [\"00\", \"11\"])\n",
" \n",
" # 取偏迹除去测量后的量子比特 A1&B1\n",
" # 把 Alice 还有 Bob 的第一个量子比特 A0&B0 作为寄存器\n",
" status_fin = self.partial_state(status_mid, [('Alice', 0), ('Bob', 0)])\n",
" \n",
" return status_fin"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:13:18.897415Z",
"start_time": "2021-01-27T06:13:18.768481Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"输入态的保真度是: 0.77500\n",
"蒸馏后的量子态保真度是: 0.81376\n",
"蒸馏成功的概率是: 74.500%\n",
"========================================================\n",
"输出态是:\n",
" [[0.48489933+0.j 0. +0.j 0. +0.j 0.32885906+0.j]\n",
" [0. +0.j 0.01510067+0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0.01510067+0.j 0. +0.j]\n",
" [0.32885906+0.j 0. +0.j 0. +0.j 0.48489933+0.j]]\n",
"初始 logarithmic negativity 是: 0.6322682154995127\n",
"蒸馏后 logarithmic negativity 是: 0.7026724166123284\n"
]
}
],
"source": [
"with fluid.dygraph.guard():\n",
" \n",
" # 运行 BBPSSW 协议\n",
" status_fin = LOCC().BBPSSW()\n",
" \n",
" # 计算保真度\n",
" target_state = fluid.dygraph.to_variable(bell_state(2))\n",
" fidelity = 0\n",
" for status in status_fin:\n",
" fidelity += trace(matmul(target_state, status.state)).real\n",
" fidelity /= len(status_fin)\n",
" \n",
" # 计算成功概率\n",
" suc_rate = sum([status.prob for status in status_fin])\n",
" fidelity_in,_,_ = BBPSSW_metrics(p)\n",
" \n",
" # 输出模拟结果\n",
" print(f\"输入态的保真度是: {fidelity_in:.5f}\")\n",
" print(f\"蒸馏后的量子态保真度是: {fidelity.numpy()[0]:.5f}\")\n",
" print(f\"蒸馏成功的概率是: {suc_rate.numpy()[0]:#.3%}\")\n",
" \n",
" # 打印输出态\n",
" rho_out = status_fin[0].state.numpy()\n",
" print(\"========================================================\")\n",
" print(f\"输出态是:\\n {rho_out}\")\n",
" print(f\"初始 logarithmic negativity 是: {logarithmic_negativity(isotropic_state(2,p))}\")\n",
" print(f\"蒸馏后 logarithmic negativity 是: {logarithmic_negativity(rho_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 结论\n",
"\n",
"可以看到,模拟结果与理论预测完全吻合。 通过 BBPSSW 协议,我们可以从两个低保真度的纠缠态中提取具有更高保真度的纠缠态。\n",
"\n",
"**优点**\n",
"- 操作很简单,仅需要 CNOT 门。\n",
"- 成功概率高。\n",
"\n",
"**缺点**\n",
"- 对输入态类型的严格要求。 要求 Isotropic 态。\n",
"- 可拓展性较差,无法直接扩展到多个拷贝的情况。\n",
"\n",
"我们建议感兴趣的读者可以接着浏览以下教程学习 [如何通过 LOCCNet 设计全新的纠缠蒸馏方案](./EntanglementDistillation_LOCCNET_CN.ipynb)。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## 参考文献\n",
"\n",
"[1] Chitambar, Eric, et al. \"Everything you always wanted to know about LOCC (but were afraid to ask).\" [Communications in Mathematical Physics 328.1 (2014): 303-326.](https://link.springer.com/article/10.1007/s00220-014-1953-9)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[3] Bennett, Charles H., et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 70.13 (1993): 1895.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895)\n",
"\n",
"[4] Bennett, Charles H., and Stephen J. Wiesner. \"Communication via one-and two-particle operators on Einstein-Podolsky-Rosen states.\" [Physical Review Letters 69.20 (1992): 2881.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.69.2881)\n",
"\n",
"[5] Ekert, Artur K. \"Quantum cryptography based on Bell’s theorem.\" [Physical Review Letters 67.6 (1991): 661.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.67.661)\n",
"\n",
"[6] Życzkowski, Karol, et al. \"Volume of the set of separable states.\" [Physical Review A 58.2 (1998): 883.](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.58.883)\n",
"\n",
"[7] Peres, Asher. \"Separability criterion for density matrices.\" [Physical Review Letters 77.8 (1996): 1413.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.1413)\n",
"\n",
"[8] Gühne, Otfried, and Géza Tóth. \"Entanglement detection.\" [Physics Reports 474.1-6 (2009): 1-75.](https://www.sciencedirect.com/science/article/abs/pii/S0370157309000623)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entanglement Distillation -- BBPSSW Protocol\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"Quantum entanglement plays a vital role in quantum communication, quantum computing, and many other quantum technologies. Therefore, detecting, transmitting, and distributing quantum entanglement reliably are essential tasks if we want to build real applications in those fields. However, errors are inevitable in the real world. They could come from imperfect equipment when we create entanglement (preparation errors), or the quantum channel used to transmit entanglement is noisy, and we gradually lose the degree of entanglement as the transmission distance increases. The aim of entanglement distillation is to compensate for those losses and restore a **maximally entangled state** at the cost of many noisy entangled states. In this sense, one could also refer entanglement distillation as a purification/error-correction protocol. This process often involves two remote parties Alice and Bob such that only Local Operations and Classical Communication (LOCC) are allowed [1]. The concept of entanglement distillation was first introduced by Bennett et al. [2] in 1996 and the original protocol is known as the **BBPSSW protocol** following the name of the authors."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"### The Bell states\n",
"\n",
"Many protocols in quantum information processing utilize the Bell states, including quantum teleportation [3], superdense coding [4] and quantum cryptography based on Bell’s theorem [5]. Such an enormous application range makes the four bell states an important subject of study. They are usually defined in the following bi-partite notation,\n",
"\n",
"$$ \n",
"\\begin{align*}\n",
"|\\Phi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|0\\rangle_B \\pm |1\\rangle_A\\otimes|1\\rangle_B), \\\\\n",
"|\\Psi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|1\\rangle_B \\pm |1\\rangle_A\\otimes|0\\rangle_B). \n",
"\\tag{1}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"where $A$ (Alice) holds one qubit and $B$ (Bob) holds the other qubit. They could be physically separated. These states can not be decomposed as a single product state $|\\varphi\\rangle_A\\otimes |\\psi\\rangle_B$. **Note: This is a key feature of entangled pure states**. \n",
"\n",
"There is an interesting feature about Bell state that if trace out subsystem $A$, the reduced density operator $\\rho_B$ will become $I/2$. For example, let's take a look at the Bell state $|\\Phi^{+}\\rangle_{AB}$\n",
"\n",
"$$\n",
"\\begin{align*}\n",
"\\rho_B &\\equiv \\text{tr}_A(\\Phi^{+}_{AB}), \\\\\n",
"& = \\text{tr}_A(|\\Phi^{+}\\rangle\\langle\\Phi^+|_{AB}), \\\\\n",
"& = \\frac{1}{2}\\text{tr}_A\\big(|00\\rangle\\langle00|+|00\\rangle\\langle11|+|11\\rangle\\langle00|+|11\\rangle\\langle11|\\big),\\\\\n",
"& = \\frac{1}{2} \\big(\\text{tr}_A(|0\\rangle\\langle0|_A)\\cdot|0\\rangle\\langle0|_B + \\text{tr}_A(|0\\rangle\\langle1|_A)\\cdot|0\\rangle\\langle1|_B \\\\\n",
"& \\quad\\quad + \\text{tr}_A(|1\\rangle\\langle0|_A) \\cdot|1\\rangle\\langle0|_B+\n",
"\\text{tr}_A(|1\\rangle\\langle1|_A)\\cdot|1\\rangle\\langle1|_B \\big), \\\\\n",
"& = \\frac{1}{2} (|0\\rangle\\langle0|_B + |1\\rangle\\langle1|_B) = \\frac{1}{2} I.\n",
"\\tag{2}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"There exists a non-classical correlation between the entangled qubit pair. We cannot describe the whole system with side information about the sub-systems. One can create the Bell states $\\Phi^{+}_{AB}$ (density matrix) easily in Paddle Quantum by calling the function `bell_state()`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:09.977831Z",
"start_time": "2021-01-26T16:21:09.281510Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The generated state Phi = \n",
" [[0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]\n",
" [0. +0.j 0. +0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0. +0.j 0. +0.j]\n",
" [0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]]\n"
]
}
],
"source": [
"from paddle_quantum.state import bell_state\n",
"N = 2 # number of qubits\n",
"rho = bell_state(N)\n",
"print(\"The generated state Phi = \\n\", rho)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Entanglement quantification\n",
"\n",
"After having a taste of quantum entanglement qualitatively, we want to promote our understanding to a quantitive level. One should realize the validity of aforementioned quantum communication protocols, including quantum teleportation and superdense coding, depends on **the quality of the entanglement quantification**. Following the convention in quantum information community, the **Negativity** $\\mathcal{N}(\\rho)$ and the **Logarithmic Negativity** $E_{N}(\\rho)$ are widely recognized metrics to quantify the amount of entanglement presented in a bi-partite system [6]. Specifically,\n",
"\n",
"$$\n",
"\\mathcal{N}(\\rho) \\equiv \\frac{||\\rho^{T_B}||_1-1}{2},\n",
"\\tag{3}\n",
"$$\n",
"\n",
"and\n",
"\n",
"$$\n",
"E_{N}(\\rho) \\equiv \\text{log}_2 ||\\rho^{T_B}||_1,\n",
"\\tag{4}\n",
"$$\n",
"\n",
"where $T_B$ stands for the partial transpose operation with respect to subsystem $B$ and can be defined as [7],\n",
"\n",
"$$\n",
"\\rho_{A B}^{T_{B}}:=(I \\otimes T)(\\rho_{A B})=\\sum_{i, j}(I_{A} \\otimes|i\\rangle\\langle j|_{B}) \\rho_{A B}(I_{A} \\otimes|i\\rangle\\langle j|_{B}).\n",
"\\tag{5}\n",
"$$\n",
"\n",
"\n",
"$||\\cdot||_1$ denotes the trace norm and $\\{|i\\rangle_B\\}$ is a basis of subsystem $B$. It is easy to connect these two metrics as $E_{N}(\\rho) = \\text{log}_2 (2\\mathcal{N}(\\rho)+1)$. They are all related to the PPT criterion for separability. \n",
"\n",
"> *A density matrix has a positive partial transpose (PPT) if its partial transposition has no negative eigenvalues (i.e. positive semidefinite). Otherwise, it's called NPT.*\n",
"\n",
"The reason why we introduce such metrics and criterion is not only helping us better understand quantum entanglement but also guiding us in distillation protocols. **Note: All PPT states are NOT distillable**. This means we can not use many copies of the same PPT states to reach a state closer to the maximally entangled state through LOCC in the asymptotic regime. So, it is always a good idea to check the PPT condition before we put the initial state into distillation protocols. For a detailed discussion on the relation between the positive map theory and separability, please refer to this review paper [8]. All these concepts are coded in Paddle Quantum by calling the functions `negativity()`,`logarithmic_negativity()`, and `partial_transpose()`.\n",
"\n",
"\n",
"\n",
"After introducing many new concepts, we provide a concrete example. For the specific Bell state $|\\Phi^{+}\\rangle_{AB}$, we have\n",
"\n",
"$$\n",
"\\begin{align*}\n",
"\\rho^{T_{B}} &= \\frac{1}{2}\n",
"\\big(|00\\rangle\\langle00|+|01\\rangle\\langle10|+|10\\rangle\\langle01|+|11\\rangle\\langle11|\\big),\\\\\n",
"& = \\frac{1}{2}\n",
"\\begin{bmatrix}\n",
"1 & 0 & 0 & 0\\\\\n",
"0 & 0 & 1 & 0\\\\\n",
"0 & 1 & 0 & 0\\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{bmatrix}\n",
".\n",
"\\tag{6}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"with a negative eigenvalue $\\lambda_{-}(\\rho^{T_{B}}) = -1/2$ and logarithmic negativity $E_{N}(\\rho) = 1$."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:16.275733Z",
"start_time": "2021-01-26T16:21:10.519841Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The transposed state is\n",
" [[0.5+0.j 0. +0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0.5+0.j 0. +0.j]\n",
" [0. +0.j 0.5+0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0. +0.j 0.5+0.j]]\n",
"With negativity = 0.5 and logarithmic_negativity = 1.0\n",
"The input state satisfies the PPT condition and hence not distillable? False\n"
]
}
],
"source": [
"from paddle_quantum.state import bell_state\n",
"from paddle_quantum.utils import negativity, logarithmic_negativity, partial_transpose, is_ppt\n",
"\n",
"input_state = bell_state(2)\n",
"transposed_state = partial_transpose(input_state, 2) # 2 stands for qubit numbers\n",
"print(\"The transposed state is\\n\", transposed_state)\n",
"\n",
"neg = negativity(input_state)\n",
"log_neg = logarithmic_negativity(input_state)\n",
"print(\"With negativity =\", neg,\"and logarithmic_negativity =\", log_neg)\n",
"print(\"The input state satisfies the PPT condition and hence not distillable?\", is_ppt(input_state))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the context of entanglement distillation, we usually use the **state fidelity** $F$ between the distilled state $\\rho_{out}$ and the Bell state $|\\Phi^+\\rangle$ to quantify the performance of a distillation protocol, where\n",
"\n",
"$$\n",
"F(\\rho_{out}, \\Phi^+) \\equiv \\langle \\Phi^+|\\rho_{out}|\\Phi^+\\rangle.\n",
"\\tag{7}\n",
"$$\n",
"\n",
"Recall the target state in distillation task is $|\\Phi^+\\rangle$. It is natural to choose state fidelity as the metric to quantify the distance between the current state and the target state. To calculate state fidelity, we can call the function `state_fidelity(rho, sigma)`.\n",
"\n",
"$$\n",
"F_s(\\rho,\\sigma) \\equiv \\text{tr}\\big( \\sqrt{\\sqrt{\\rho}\\sigma \\sqrt{\\rho}} \\big).\n",
"\\tag{8}\n",
"$$\n",
"\n",
"Notice they are differed by a square root $F=F_s^2$."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:19.607822Z",
"start_time": "2021-01-26T16:21:19.578880Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The state fidelity between these two states are:\n",
"F= 0.7750000000000004\n"
]
}
],
"source": [
"from paddle_quantum.state import bell_state, isotropic_state\n",
"from paddle_quantum.utils import state_fidelity\n",
"\n",
"F = state_fidelity(bell_state(2), isotropic_state(2, 0.7))\n",
"print(\"The state fidelity between these two states are:\\nF=\", F**2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With many preparations, we can finally discuss the famous BBPSSW protocol in the next section."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## BBPSSW protocol\n",
"\n",
"The BBPSSW protocol distills two identical quantum states $\\rho_{in}$ (2-copies) into a single final state $\\rho_{out}$ with a higher state fidelity $F$ (closer to the Bell state $|\\Phi^+\\rangle$). It is worth noting that BBPSSW was mainly designed to purify **Isotropic states** (also known as Werner state), a parametrized family of mixed states consist of $|\\Phi^+\\rangle$ and the completely mixed state (white noise) $I/4$.\n",
"\n",
"$$\n",
"\\rho_{\\text{iso}}(p) = p\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + (1-p)\\frac{I}{4}, \\quad p \\in [0,1]\n",
"\\tag{9}\n",
"$$\n",
"\n",
"This state is PPT and hence not distillable when $p \\leq 1/3$. \n",
"\n",
"We have two remote parties, $A$ (Alice) and $B$ (Bob), involved in the BBPSSW distillation protocol. They share two qubit pairs $\\rho_{A_0B_0}$ and $\\rho_{A_1B_1}$. Each pair is initialized as $\\rho_{in} = \\rho_{\\text{iso}}(p)$. These states could come from last distillation process or previously distributed and stored in some memory registers of a quantum network. Alice holds two qubits $A_0, A_1$, and Bob also holds two qubits $B_0, B_1$. With these initial setups, Alice and Bob implements the following LOCC process:\n",
"\n",
"1. Alice and Bob firstly choose the qubit pair **they want to keep as the memory qubit pair to store entanglement resource after distillation**. Here, they choose $A_0$ and $B_0$. \n",
"2. Alice and Bob both apply a CNOT gate on their qubits. Here, they choose $A_0,B_0$ as the control qubits and $A_1,B_1$ as the target qubits.\n",
"3. Two remote parties measure the target qubits then use classical communication channel to exchange their measurement results $m_{A_1}, m_{B_1}$.\n",
"4. If the measurement results of Alice and Bob are the same (00 or 11), the distillation is successful, and the qubit pair $A_0, B_0$ is stored as state $\\rho_{out}$; if the measurement results are different (01 or 10), they claim the distillation failed and the qubit pair $A_0, B_0$ will be discarded.\n",
"\n",
"<center><img src=\"figures/distillation-fig-BBPSSW.png\" height=\"200\" width=\"400\"></center>\n",
"<div style=\"text-align:center\">Figure 1: Schematic diagram of the BBPSSW protocol </div>\n",
"\n",
"\n",
"**Note:** The distillation protocol is probabilistic and it does not succeed every time. So, another metric to quantify the performance of distillation protocol is **the probability of success $p_{succ}$**. In general, there exists a trade off between the final state fidelity $F' = F(\\rho_{out},\\Phi^+)$ and $p_{succ}$. Theoretically, these two specific metrics for the BBPSSW protocol are provided as,\n",
"\n",
"\n",
"$$\n",
"F^{\\prime}=\\frac{F^2 + \\frac{1}{9}(1-F)^2}{F^2 + \\frac{2}{3}F(1-F) + \\frac{5}{9}(1-F)^2},\n",
"\\tag{10}\n",
"$$\n",
"\n",
"where the initial fidelity $F(\\rho_{in},\\Phi^+) = (1+3p)/4$ with a probability of success,\n",
"\n",
"$$\n",
"p_{\\text {succ }}=F^{2} + \\frac{2}{3}F(1-F) + \\frac{5}{9}(1-F)^2.\n",
"\\tag{11}\n",
"$$\n",
"\n",
"In the next section, we will go through how to simulate the BBPSSW protocol with Paddle Quantum.\n",
"\n",
"**Note:** If the input state is not an isotropic state, one can depolarize it to an isotropic state with a twirling operation."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulation with Paddle Quantum \n",
"\n",
"First, we need to import all the dependencies:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:22.649950Z",
"start_time": "2021-01-26T16:21:22.011428Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.state import bell_state, isotropic_state\n",
"from paddle_quantum.utils import logarithmic_negativity, is_ppt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For convenience, we introduce the metrics of BBPSSW here and later compare with the simulation result."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:23.353264Z",
"start_time": "2021-01-26T16:21:23.344765Z"
}
},
"outputs": [],
"source": [
"def BBPSSW_metrics(p):\n",
" \"\"\"\n",
" Returns output fidelity and probability of success of the BBPSSW protocol.\n",
" \"\"\"\n",
" F_in = (1+3*p)/4\n",
" p_succ = F_in**2 + 2*F_in*(1-F_in)/3+ 5*(1-F_in)**2/9\n",
" F_out = (F_in**2 + (1-F_in)**2/9)/p_succ\n",
" \n",
" return F_in, F_out, p_succ"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In our example, the input state is an isotropic state with $p=0.7$, that is\n",
"\n",
"$$\n",
"\\rho_{in} = \\rho_{\\text{iso}}(0.7)= 0.7\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + 0.075 I.\n",
"\\tag{12}\n",
"$$\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:24.513396Z",
"start_time": "2021-01-26T16:21:24.504698Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The input fidelity is: 0.7749999999999999\n",
"The output fidelity is: 0.8137583892617447\n",
"With a probability of success: 0.745\n",
"The input state satisfies the PPT condition and hence not distillable? False\n"
]
}
],
"source": [
"p = 0.7\n",
"F_in, F_out, p_succ = BBPSSW_metrics(p)\n",
"print(\"The input fidelity is:\", F_in)\n",
"print(\"The output fidelity is:\", F_out)\n",
"print(\"With a probability of success:\", p_succ)\n",
"print(\"The input state satisfies the PPT condition and hence not distillable?\", is_ppt(isotropic_state(2, p)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we can simulate the BBPSSW protocol and check if the results match with theory."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T16:21:26.904523Z",
"start_time": "2021-01-26T16:21:26.696414Z"
},
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The fidelity of the input quantum state is: 0.77500\n",
"The fidelity of the purified quantum state is: 0.81376\n",
"The probability of successful purification is: 74.500%\n",
"========================================================\n",
"The output state is:\n",
" [[0.48489933+0.j 0. +0.j 0. +0.j 0.32885906+0.j]\n",
" [0. +0.j 0.01510067+0.j 0. +0.j 0. +0.j]\n",
" [0. +0.j 0. +0.j 0.01510067+0.j 0. +0.j]\n",
" [0.32885906+0.j 0. +0.j 0. +0.j 0.48489933+0.j]]\n",
"The initial logarithmic negativity is: 0.6322682154995127\n",
"The final logarithmic negativity is: 0.7026724166123284\n"
]
}
],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # Add the first party Alice \n",
" # The first parameter 2 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(2, party_name='Alice')\n",
" \n",
" # Add the second party Bob\n",
" # The first parameter 2 stands for how many qubits B holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(2, party_name='Bob')\n",
" \n",
" # Define the input quantum states rho_in\n",
" _state = fluid.dygraph.to_variable(isotropic_state(2, p))\n",
" \n",
" # ('Alice', 0) means Alice's first qubit A0\n",
" # ('Bob', 0) means Bob's first qubit B0\n",
" self.set_init_status(_state, [('Alice', 0), ('Bob', 0)]) \n",
" \n",
" # ('Alice', 1) means Alice's second qubit A1\n",
" # ('Bob', 1) means Bob's second qubit B1\n",
" self.set_init_status(_state, [('Alice', 1), ('Bob', 1)]) \n",
" \n",
" def BBPSSW(self):\n",
" status = self.init_status\n",
" \n",
" # Create Alice's local operations \n",
" cir1 = self.create_ansatz('Alice')\n",
" cir1.cnot([0, 1])\n",
"\n",
" # Create Bob's local operations \n",
" cir2 = self.create_ansatz('Bob')\n",
" cir2.cnot([0, 1])\n",
" \n",
" # Run circuit\n",
" status = cir1.run(status)\n",
" status_mid = cir2.run(status)\n",
" \n",
" # ('Alice', 1) means measuring Alice's second qubit A1\n",
" # ('Bob', 1) means measuring Bob's second qubit B1\n",
" # ['00','11'] specifies the success condition for distillation\n",
" # Means Alice and Bob both measure '00' or '11'\n",
" status_mid = self.measure(status_mid, [('Alice', 1), ('Bob', 1)], [\"00\", \"11\"])\n",
" \n",
" # Trace out the measured qubits A1&B1\n",
" # Leaving only Alice’s first qubit and Bob’s first qubit A0&B0 as the memory register\n",
" status_fin = self.partial_state(status_mid, [('Alice', 0), ('Bob', 0)])\n",
" \n",
" return status_fin\n",
" \n",
"\n",
"# Turn on dynamic computational graph\n",
"with fluid.dygraph.guard():\n",
" \n",
" # Run BBPSSW protocol\n",
" status_fin = LOCC().BBPSSW()\n",
" \n",
" # Calculate fidelity\n",
" target_state = fluid.dygraph.to_variable(bell_state(2))\n",
" fidelity = 0\n",
" for status in status_fin:\n",
" fidelity += trace(matmul(target_state, status.state)).real\n",
" fidelity /= len(status_fin)\n",
" \n",
" # Calculate success rate\n",
" suc_rate = sum([status.prob for status in status_fin])\n",
" fidelity_in,_,_ = BBPSSW_metrics(p)\n",
" \n",
" # Output simulation results\n",
" print(f\"The fidelity of the input quantum state is: {fidelity_in:.5f}\")\n",
" print(f\"The fidelity of the purified quantum state is: {fidelity.numpy()[0]:.5f}\")\n",
" print(f\"The probability of successful purification is: {suc_rate.numpy()[0]:#.3%}\")\n",
" \n",
" # Print the output state\n",
" rho_out = status_fin[0].state.numpy()\n",
" print(\"========================================================\")\n",
" print(f\"The output state is:\\n {rho_out}\")\n",
" print(f\"The initial logarithmic negativity is: {logarithmic_negativity(isotropic_state(2,p))}\")\n",
" print(f\"The final logarithmic negativity is: {logarithmic_negativity(rho_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"As we can see, the simulation results matches the theoretical prediction perfectly. Through the BBPSSW protocol, we can extract an entangled state with higher Fidelity from two entangled states with low Fidelity.\n",
"\n",
"**Advantages**\n",
"- Operations are simple. Only CNOT gates are required.\n",
"- High success rate.\n",
"\n",
"**Disadvantages**\n",
"- Strict requirements for the input state type. Isotropic states are preferred.\n",
"- Poor scalability. Unable to directly extend to the case of multiple copies.\n",
"\n",
"We suggest interested readers to check the tutorial on [how to design a new distillation protocol with LOCCNet](./EntanglementDistillation_LOCCNet_EN.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## References\n",
"\n",
"[1] Chitambar, Eric, et al. \"Everything you always wanted to know about LOCC (but were afraid to ask).\" [Communications in Mathematical Physics 328.1 (2014): 303-326.](https://link.springer.com/article/10.1007/s00220-014-1953-9)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[3] Bennett, Charles H., et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 70.13 (1993): 1895.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895)\n",
"\n",
"[4] Bennett, Charles H., and Stephen J. Wiesner. \"Communication via one-and two-particle operators on Einstein-Podolsky-Rosen states.\" [Physical Review Letters 69.20 (1992): 2881.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.69.2881)\n",
"\n",
"[5] Ekert, Artur K. \"Quantum cryptography based on Bell’s theorem.\" [Physical Review Letters 67.6 (1991): 661.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.67.661)\n",
"\n",
"[6] Życzkowski, Karol, et al. \"Volume of the set of separable states.\" [Physical Review A 58.2 (1998): 883.](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.58.883)\n",
"\n",
"[7] Peres, Asher. \"Separability criterion for density matrices.\" [Physical Review Letters 77.8 (1996): 1413.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.1413)\n",
"\n",
"[8] Gühne, Otfried, and Géza Tóth. \"Entanglement detection.\" [Physics Reports 474.1-6 (2009): 1-75.](https://www.sciencedirect.com/science/article/abs/pii/S0370157309000623)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 纠缠蒸馏 -- DEJMPS 协议\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概述\n",
"\n",
"如果读者对纠缠蒸馏的基本概念不熟悉,请浏览我们之前关于 [BBPSSW](./EntanglementDistillation_BBPSSW_CN.ipynb) 的教程。在之前的教程中,我们对纠缠蒸馏的 BBPSSW 协议 [1] 进行了介绍。本教程将介绍 DEJMPS 协议,该协议是由 Deutsch 等人提出的 [2],和 BBPSSW 协议有着类似的原理。这两种协议最主要的区别是:DEJMPS 协议可以对贝尔对角态(Bell-diagonal state)进行蒸馏,而 BBPSSW 只能保证在 isotropic 态上有效。下面,我们对 DEJMPS 协议进行详细介绍。\n",
"\n",
"在纠缠蒸馏中,我们关心的问题是:如何通过 LOCC 操作,从多个含有噪声的纠缠量子比特对中生成一个高保真度的**最大纠缠态(maximally-entangled state)** $|\\Phi^+\\rangle$,也称之为贝尔态。让我们先回顾一下贝尔态的定义,\n",
"\n",
"$$ \n",
"\\begin{align*}\n",
"|\\Phi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|0\\rangle_B \\pm |1\\rangle_A\\otimes|1\\rangle_B), \\\\\n",
"|\\Psi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|1\\rangle_B \\pm |1\\rangle_A\\otimes|0\\rangle_B). \n",
"\\tag{1}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"$A$ 和 $B$ 代表的是共享纠缠对的双方 Alice 和 Bob。根据贝尔对角态(Bell-digonal state)的定义,在贝尔态作为基底的密度矩阵可以表示为如下对角形式,\n",
"\n",
"$$\n",
"\\rho_{\\text{diag}} = p_1 | \\Phi^+\\rangle \\langle \\Phi^+ | + p_2 | \\Psi^+\\rangle \\langle \\Psi^+ | + \n",
"p_3 | \\Phi^-\\rangle \\langle \\Phi^- | + p_4 | \\Psi^-\\rangle \\langle \\Psi^- |.\n",
"\\tag{2}\n",
"$$\n",
"\n",
"这里我们假设 $p_1 > p_2 \\geq p_3 \\geq p_4$,并且满足 $p_1 + p_2+ p_3+ p_4 = 1$。那么,我们可以通过以下方式刻画贝尔对角态的纠缠度:\n",
"\n",
"* 保真度 (State fidelity):$F = \\langle \\Phi^+|\\rho_{\\text{diag}}|\\Phi^+\\rangle = p_1$\n",
"* Negativity: $\\mathcal{N}(\\rho_{\\text{diag}}) = p_1 - 1/2$\n",
"\n",
"**提示:** 贝尔对角态仅能在 $p_1 > 1/2$ 时才能通过蒸馏提高保真度。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## DEJMPS 协议\n",
"\n",
"假设 Alice ($A$) 和 Bob ($B$) 持有两对纠缠量子比特,我们分别将它们标记为 $\\{ A_0, B_0 \\}, \\{ A_1, B_1 \\}$。并且这两对量子比特都分别处于相同的贝尔对角态 $\\rho_{diag}$,它们的 $p_1 > 0.5$。那么我们就可以通过如下的流程来蒸馏这两对量子比特,使得输出态和贝尔态 $|\\Phi^+\\rangle$ 之间的保真度更高:\n",
"\n",
"1. Alice 和 Bob 先选择任意一对量子比特,**这对量子比特将作为最终的输出比特**。这里假设他们选择了 $A_0$ 和 $B_0$。\n",
"2. 首先,Alice 对她的两个量子比特施加旋转门 $R_x(\\pi/2)$ ,Bob对他的量子比特施加旋转门 $R_x(-\\pi/2)$。\n",
"3. 之后,Alice 和 Bob 对他们手中的量子比特施加受控非门 (CNOT 门)。这里, $A_0$ 和 $B_0$ 作为控制比特,$A_1$ 和 $B_1$ 作为目标比特。\n",
"4. 接下来双方分别对 $A_1$ 和 $B_1$ 进行测量,并通过**经典通讯**来交换他们的测量结果 $m_{A_1}, m_{B_1}$。\n",
"5. 如果Alice和Bob的结果一致(00或11),那么他们可以宣布本次蒸馏过程成功,同时 $A_0$ 和 $B_0$ 作为输出比特,输出态为 $\\rho_{out}$。相反,如果他们的测量结果不一致(01或10),那么本次蒸馏过程失败,丢弃量子比特 $A_0$ 和 $B_0$。\n",
"\n",
"<center><img src=\"figures/distillation-fig-DEJMPS.jpg\" height=\"250\" width=\"300\"></center>\n",
"<div style=\"text-align:center\">图1:DEJMPS 纠缠蒸馏过程电路图 </div>\n",
"\n",
"在蒸馏成功的情况下,$A_0$ 和 $B_0$ 作为输出比特,他们的输出态 $\\rho_{out}$ 的和目标态之间的保真度将会提升。输出态的保真度 $F_{out}$ 为\n",
"\n",
"$$\n",
"F_{out} = \\frac{p_1^2 + p_4^2}{(p_1 + p_4)^2 + (p_2 + p_3)^2},\n",
"\\tag{3}\n",
"$$\n",
"\n",
"与 BBPSSW 协议相似, DEJMPS 协议并不能保证每次蒸馏都会成功,他的成功率 $p_{succ}$ 是\n",
"\n",
"$$ \n",
"p_{succ} = (p_1 + p_4)^2 + (p_2 + p_3)^2.\n",
"\\tag{4}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Paddle Quantum 代码实现\n",
"\n",
"首先,我们导入相关的包"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:10:17.461722Z",
"start_time": "2021-01-27T06:10:09.975500Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.state import bell_state, isotropic_state, bell_diagonal_state\n",
"from paddle_quantum.utils import negativity, logarithmic_negativity, is_ppt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在使用量子电路模拟之前,先计算一下对于如下输入态的蒸馏结果的理论值:\n",
"\n",
"$$\n",
"\\rho = p_1 | \\Phi^+\\rangle \\langle \\Phi^+ | + \\frac{1-p_1}{2} | \\Psi^+\\rangle \\langle \\Psi^+ |+ \n",
"\\frac{1-p_1}{3}| \\Phi^-\\rangle \\langle \\Phi^- | + \\frac{1-p_1}{6} | \\Psi^-\\rangle \\langle \\Psi^- |.\n",
"\\tag{5}\n",
"$$\n",
"\n",
"假设我们令 $p_1 = 0.7$,那么保真度的提升以及蒸馏成功的概率可以可以被如下函数计算:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:10:17.561117Z",
"start_time": "2021-01-27T06:10:17.465773Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"输入保真度是: 0.7\n",
"蒸馏后的保真度的理论值为: 0.7879999999999999\n",
"蒸馏成功率的理论值为: 0.625\n",
"输入态是否满足 PPT 条件并因而无法进行蒸馏? False\n"
]
}
],
"source": [
"def DEJMPS_metrics(*p):\n",
" \"\"\"\n",
" 返回 DEJMPS 协议蒸馏成功后的保真度以及成功率的理论值.\n",
" \"\"\"\n",
" F_in = p[0]\n",
" p_succ = (p[0] + p[3]) ** 2 + (p[1] + p[2]) ** 2\n",
" F_out = (p[0] ** 2 + p[3] ** 2)/p_succ\n",
" \n",
" return F_in, F_out, p_succ\n",
"\n",
"p = 0.7\n",
"F_in, F_out, p_succ = DEJMPS_metrics(p, (1-p)/2, (1-p)/3, (1-p)/6)\n",
"print(\"输入保真度是:\", F_in)\n",
"print(\"蒸馏后的保真度的理论值为:\", F_out)\n",
"print(\"蒸馏成功率的理论值为:\", p_succ)\n",
"print(\"输入态是否满足 PPT 条件并因而无法进行蒸馏?\", \n",
" is_ppt(bell_diagonal_state(p, (1-p)/2, (1-p)/3, (1-p)/6)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"接下来,我们用 Paddle Quantum 中的 LOCCNet 模块创建 DEJMPS 协议对应的电路,并观察他的输出结果:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T06:10:17.887395Z",
"start_time": "2021-01-27T06:10:17.588579Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"输入态的保真度为: 0.70000\n",
"蒸馏后的保真度为: 0.78800\n",
"蒸馏的成功率为: 62.500%\n",
"========================================================\n",
"蒸馏后的态是:\n",
" [[ 0.45 +0.j 0. -0.j -0. +0.j 0.338-0.j]\n",
" [ 0. +0.j 0.05 +0.j 0.002-0.j 0. -0.j]\n",
" [ 0. +0.j 0.002+0.j 0.05 -0.j 0. -0.j]\n",
" [ 0.338+0.j -0. -0.j 0. +0.j 0.45 +0.j]]\n",
"初始态的 negativity: 0.19999999999999993\n",
"输出态的 negativity: 0.28800000000000003\n"
]
}
],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # 添加第一个持有者 Alice\n",
" # 第一个参数 2 表明该持有者有多少个量子比特\n",
" # 第二个参数用来标明该持有者的名字\n",
" self.add_new_party(2, party_name='Alice')\n",
" \n",
" # 添加第二个持有者 Bob\n",
" self.add_new_party(2, party_name='Bob')\n",
" \n",
" # 定义一个贝尔对角态,四个系数对应 p1, p2, p3, p4\n",
" _state = fluid.dygraph.to_variable(bell_diagonal_state(p, (1-p)/2, (1-p)/3, (1-p)/6))\n",
" \n",
" # ('Alice', 0) 表示 Alice 的第一个量子比特 A0\n",
" # ('Bob', 0) 表示 Bob 的第一个量子比特 B0\n",
" self.set_init_status(_state, [('Alice', 0), ('Bob', 0)]) \n",
" \n",
" # ('Alice', 1) 表示 Alice 的第二个量子比特 A1\n",
" # ('Bob', 1) 表示 Bob 的第二个量子比特 B1\n",
" self.set_init_status(_state, [('Alice', 1), ('Bob', 1)]) \n",
" \n",
" # 创建两个参数 theta1 和 theta2 用来分别存储 Alice 和 Bob 的本地旋转门的角度\n",
" self.theta1 = fluid.dygraph.to_variable(np.array([np.pi/2, np.pi/2], dtype='float64'))\n",
" self.theta2 = fluid.dygraph.to_variable(np.array([-np.pi/2, -np.pi/2], dtype='float64'))\n",
" \n",
" def DEJMPS(self):\n",
" status = self.init_status\n",
" \n",
" # 用电路模拟 Alice 的本地操作 \n",
" cir1 = self.create_ansatz('Alice')\n",
" cir1.rx(self.theta1[0], 0)\n",
" cir1.rx(self.theta1[1], 1)\n",
" cir1.cnot([0, 1])\n",
"\n",
" # 用电路模拟 Bob 的本地操作 \n",
" cir2 = self.create_ansatz('Bob')\n",
" cir2.rx(self.theta2[0], 0)\n",
" cir2.rx(self.theta2[1], 1)\n",
" cir2.cnot([0, 1])\n",
" \n",
" # 运行电路\n",
" status = cir1.run(status)\n",
" status_mid = cir2.run(status)\n",
" \n",
" # 参数 ('Alice', 1) 表示测量 Alice 的第二个比特 A1\n",
" # 参数 ('Bob', 1) 表示测量 Bob 的第二个比特 B1\n",
" # 参数 ['00','11'] 表示我们希望的测量结果\n",
" # status_mid 保留了测量得到 '00' 和 '11' 之后的四比特态\n",
" status_mid = self.measure(status_mid, [('Alice', 1), ('Bob', 1)], [\"00\", \"11\"])\n",
" \n",
" # 对 A1&B1 求偏迹,用数组 [('Alice', 0), ('Bob', 0)] 表示想要保留的系统\n",
" # status_fin 即为 A0 和 B0 的二比特态\n",
" status_fin = self.partial_state(status_mid, [('Alice', 0), ('Bob', 0)])\n",
" \n",
" return status_fin\n",
" \n",
"\n",
"# 开启动态计算图\n",
"with fluid.dygraph.guard():\n",
" \n",
" # 运行 DEJMPS 协议\n",
" status_fin = LOCC().DEJMPS()\n",
" \n",
" # 生成我们想要的目标态\n",
" target_state = fluid.dygraph.to_variable(bell_state(2))\n",
" \n",
" # 计算他们之间的保真度\n",
" fidelity = 0\n",
" for status in status_fin:\n",
" fidelity += trace(matmul(target_state, status.state)).real\n",
" fidelity /= len(status_fin)\n",
" \n",
" # 计算成功率\n",
" suc_rate = sum([status.prob for status in status_fin])\n",
" \n",
" # 输出结果\n",
" print(f\"输入态的保真度为: {p:.5f}\")\n",
" print(f\"蒸馏后的保真度为: {fidelity.numpy()[0]:.5f}\")\n",
" print(f\"蒸馏的成功率为: {suc_rate.numpy()[0]:#.3%}\")\n",
" \n",
" # 输出终态\n",
" rho_out = status_fin[0].state.numpy()\n",
" print(\"========================================================\")\n",
" print(f\"蒸馏后的态是:\\n {np.around(rho_out, 4)}\")\n",
" print(f\"初始态的 negativity: {negativity(bell_diagonal_state(p, (1-p)/2, (1-p)/3, (1-p)/6))}\")\n",
" print(f\"输出态的 negativity: {negativity(rho_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"我们可以看到,模拟电路的输出结果和理论预期一致。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 结论\n",
"\n",
"DEJMPS 协议可以高效地从两对含有噪声的纠缠量子对中蒸馏出一对具有更高保真度的纠缠对。和只能蒸馏 isotropic 态的 BBPSSW 协议相比,DEJMPS 协议可以应用在任意贝尔对角态上。因为贝尔对角态本身包含了 isotropic 态,所以 DEJMPS 协议是一种比 BBPSSW 协议更通用的蒸馏协议。然而,他也和 BBPSSW 协议具有相同的缺点:仍然不能对任意输入态进行蒸馏,并且在多对量子比特的场景下没有良好的拓展性。\n",
"\n",
"我们建议感兴趣的读者可以接着浏览以下教程学习 [如何通过 LOCCNet 设计全新的纠缠蒸馏方案](./EntanglementDistillation_LOCCNET_CN.ipynb)。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## 参考资料\n",
"\n",
"[1] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[2] Deutsch, David, et al. \"Quantum privacy amplification and the security of quantum cryptography over noisy channels.\" [Physical Review Letters 77.13 (1996): 2818.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.2818)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entanglement Distillation -- DEJMPS Protocol\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"Before reading this tutorial, we highly recommend you to read the [BBPSSW protocol](./EntanglementDistillation_BBPSSW_EN.ipynb) first if you are not familiar with entanglement distillation. The DEJMPS protocol, introduced by Deutsch et al. [1], is similar to the BBPSSW protocol. The main difference between these two protocols is the state for distillation: the DEJMPS protocol can distill Bell-diagonal states, while the BBPSSW protocol could distill isotropic states. In entanglement distillation, the main purpose is to generate a **maximally entangled state** $|\\Phi^+\\rangle$ from many copies of imperfect entangled states, using only LOCC operations. Recall the four Bell states,\n",
"\n",
"$$ \n",
"\\begin{align*}\n",
"|\\Phi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|0\\rangle_B \\pm |1\\rangle_A\\otimes|1\\rangle_B), \\\\\n",
"|\\Psi^{\\pm}\\rangle_{AB} &= \\frac{1}{\\sqrt{2}}(|0\\rangle_A\\otimes|1\\rangle_B \\pm |1\\rangle_A\\otimes|0\\rangle_B). \n",
"\\tag{1}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"where $A$ and $B$ represent the bi-party Alice and Bob. The Bell-diagonal state, by definition, is diagonal in the Bell basis that can be expressed as\n",
"\n",
"$$\n",
"\\rho_{\\text{diag}} = p_1 | \\Phi^+\\rangle \\langle \\Phi^+ | + p_2 | \\Psi^+\\rangle \\langle \\Psi^+ | + \n",
"p_3 | \\Phi^-\\rangle \\langle \\Phi^- | + p_4 | \\Psi^-\\rangle \\langle \\Psi^- |.\n",
"\\tag{2}\n",
"$$\n",
"\n",
"with $p_1 > p_2 \\geq p_3 \\geq p_4$ and $p_1 + p_2+ p_3+ p_4 = 1$. Then the entanglement quantification of a Bell-diagonal state can be described as:\n",
"\n",
"* State fidelity $F = \\langle \\Phi^+|\\rho_{\\text{diag}}|\\Phi^+\\rangle = p_1$\n",
"* Negativity $\\mathcal{N}(\\rho_{\\text{diag}}) = p_1 - 1/2$\n",
"\n",
"**Note:** The Bell-diagonal state is distillable when $p_1 > 1/2$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## DEJMPS protocol\n",
"\n",
"Suppose that two parties, namely Alice($A$) and Bob($B$), possess two copies of entangled qubit: $\\{ A_0, B_0 \\}, \\{ A_1, B_1 \\}$. If these two pairs are all in the same Bell-diagonal state $\\rho_{\\text{diag}}$, with $p_1 > 0.5$. We can apply the following workflow to purify the input states and leads to an output state has fidelity closer to $|\\Phi^+\\rangle$:\n",
"1. Alice and Bob firstly choose the qubit pair **they want to keep as the memory qubit pair after distillation**. Here, they choose $A_0$ and $B_0$. \n",
"2. Alice performs $R_x(\\pi/2)$ gates on both qubits, and Bob performs $R_x(-\\pi/2)$ gates on both qubits.\n",
"3. Then, Alice and Bob both apply a CNOT gate on their qubits. Here, they choose $A_0,B_0$ as the control qubits and $A_1,B_1$ as the target qubits.\n",
"4. Two remote parties measure the target qubits and use a classical communication channel to exchange their measurement results $m_{A_1}, m_{B_1}$.\n",
"5. If the measurement results of Alice and Bob are the same (00 or 11), the distillation is successful, and the qubit pair $A_0, B_0$ is stored as state $\\rho_{out}$; If the measurement results are different (01 or 10), they claim the distillation failed and the qubit pair $A_0, B_0$ will be discarded.\n",
"\n",
"<center><img src=\"figures/distillation-fig-DEJMPS.jpg\" height=\"250\" width=\"300\"></center>\n",
"<div style=\"text-align:center\">Figure 1: Schematic diagram of the DEJMPS protocol </div>\n",
"\n",
"After the distillation, the final state $\\rho_{out}$ of entangled pair $A_0, B_0$ will have higher fidelity than the initial state $\\rho$. The fidelity of the final state $F_{out}$ is\n",
"\n",
"$$\n",
"F_{out} = \\frac{p_1^2 + p_4^2}{(p_1 + p_4)^2 + (p_2 + p_3)^2},\n",
"\\tag{3}\n",
"$$\n",
"\n",
"Similar to the BBPSSW protocol, the DEJMPS protocol is probabilistic, with the probability of a successful distillation being \n",
"\n",
"$$ \n",
"p_{succ} = (p_1 + p_4)^2 + (p_2 + p_3)^2.\n",
"\\tag{4}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulation with Paddle Quantum\n",
"First, we need to import relevant packages"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T16:59:07.977165Z",
"start_time": "2021-01-25T16:59:06.564863Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.state import bell_state, isotropic_state, bell_diagonal_state\n",
"from paddle_quantum.utils import negativity, logarithmic_negativity, is_ppt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, let us see the theoretical result of applying the **DEJMPS protocol** to the state\n",
"\n",
"$$\n",
"\\rho = p_1 | \\Phi^+\\rangle \\langle \\Phi^+ | + \\frac{1-p_1}{2} | \\Psi^+\\rangle \\langle \\Psi^+ |+ \n",
"\\frac{1-p_1}{3}| \\Phi^-\\rangle \\langle \\Phi^- | + \\frac{1-p_1}{6} | \\Psi^-\\rangle \\langle \\Psi^- |.\n",
"\\tag{5}\n",
"$$\n",
"\n",
"Suppose we take $p_1 = 0.7$, then the theoretical improvement of fidelity can be calculated by the following block:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T16:59:11.804714Z",
"start_time": "2021-01-25T16:59:11.771952Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The input fidelity is: 0.7\n",
"The output fidelity is: 0.7879999999999999\n",
"With a probability of success: 0.625\n",
"The input state satisfies the PPT condition and hence not distillable? False\n"
]
}
],
"source": [
"def DEJMPS_metrics(*p):\n",
" \"\"\"\n",
" Returns output fidelity and probability of success of the DEJMPS protocol.\n",
" \"\"\"\n",
" F_in = p[0]\n",
" p_succ = (p[0] + p[3]) ** 2 + (p[1] + p[2]) ** 2\n",
" F_out = (p[0] ** 2 + p[3] ** 2)/p_succ\n",
" \n",
" return F_in, F_out, p_succ\n",
"\n",
"p = 0.7\n",
"F_in, F_out, p_succ = DEJMPS_metrics(p, (1-p)/2, (1-p)/3, (1-p)/6)\n",
"print(\"The input fidelity is:\", F_in)\n",
"print(\"The output fidelity is:\", F_out)\n",
"print(\"With a probability of success:\", p_succ)\n",
"print(\"The input state satisfies the PPT condition and hence not distillable?\", \n",
" is_ppt(bell_diagonal_state(p, (1-p)/2, (1-p)/3, (1-p)/6)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we can simulate the DEJMPS protocol and check if the results match with the theory."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T16:59:15.223963Z",
"start_time": "2021-01-25T16:59:14.574124Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The fidelity of the input quantum state is: 0.70000\n",
"The fidelity of the purified quantum state is: 0.78800\n",
"The probability of successful purification is: 62.500%\n",
"========================================================\n",
"The output state is:\n",
" [[0.45 -0.j 0. +0.j 0. -0.j 0.338-0.j]\n",
" [0. -0.j 0.05 +0.j 0.002-0.j 0. +0.j]\n",
" [0. -0.j 0.002+0.j 0.05 -0.j 0. -0.j]\n",
" [0.338+0.j 0. +0.j 0. +0.j 0.45 -0.j]]\n",
"The initial negativity is: 0.19999999999999993\n",
"The final negativity is: 0.2880000000000001\n"
]
}
],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # Add the first party Alice \n",
" # The first parameter 2 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(2, party_name='Alice')\n",
" \n",
" # Add the second party Bob\n",
" # The first parameter 2 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(2, party_name='Bob')\n",
" \n",
" # Define the input quantum states rho_in\n",
" _state = fluid.dygraph.to_variable(bell_diagonal_state(p, (1-p)/2, (1-p)/3, (1-p)/6))\n",
" \n",
" # ('Alice', 0) means Alice's first qubit A0\n",
" # ('Bob', 0) means Bob's first qubit B0\n",
" self.set_init_status(_state, [('Alice', 0), ('Bob', 0)]) \n",
" \n",
" # ('Alice', 1) means Alice's second qubit A1\n",
" # ('Bob', 1) means Bob's second qubit B1\n",
" self.set_init_status(_state, [('Alice', 1), ('Bob', 1)]) \n",
" \n",
" # Set the angles of the Rx gates\n",
" self.theta1 = fluid.dygraph.to_variable(np.array([np.pi/2, np.pi/2], dtype='float64'))\n",
" self.theta2 = fluid.dygraph.to_variable(np.array([-np.pi/2, -np.pi/2], dtype='float64'))\n",
" \n",
" def DEJMPS(self):\n",
" status = self.init_status\n",
" \n",
" # Create Alice's local operations \n",
" cir1 = self.create_ansatz('Alice')\n",
" cir1.rx(self.theta1[0], 0)\n",
" cir1.rx(self.theta1[1], 1)\n",
" cir1.cnot([0, 1])\n",
"\n",
" # Create Bob's local operations \n",
" cir2 = self.create_ansatz('Bob')\n",
" cir2.rx(self.theta2[0], 0)\n",
" cir2.rx(self.theta2[1], 1)\n",
" cir2.cnot([0, 1])\n",
" \n",
" # Run circuit\n",
" status = cir1.run(status)\n",
" status_mid = cir2.run(status)\n",
" \n",
" # ('Alice', 1) means measuring Alice's second qubit A1\n",
" # ('Bob', 1) means measuring Bob's second qubit B1\n",
" # ['00','11'] specifies the success condition for distillation\n",
" # Means Alice and Bob both measure '00' or '11'\n",
" status_mid = self.measure(status_mid, [('Alice', 1), ('Bob', 1)], [\"00\", \"11\"])\n",
" \n",
" # Trace out the measured qubits A1&B1\n",
" # Leaving only Alice’s first qubit and Bob’s first qubit A0&B0 as the memory register\n",
" status_fin = self.partial_state(status_mid, [('Alice', 0), ('Bob', 0)])\n",
" \n",
" return status_fin\n",
" \n",
"\n",
"# Turn on dynamic computational graph\n",
"with fluid.dygraph.guard():\n",
" \n",
" # Run DEJMPS protocol\n",
" status_fin = LOCC().DEJMPS()\n",
" \n",
" # Calculate fidelity\n",
" target_state = fluid.dygraph.to_variable(bell_state(2))\n",
" fidelity = 0\n",
" for status in status_fin:\n",
" fidelity += trace(matmul(target_state, status.state)).real\n",
" fidelity /= len(status_fin)\n",
" \n",
" # Calculate success rate\n",
" suc_rate = sum([status.prob for status in status_fin])\n",
" \n",
" # Output simulation results\n",
" print(f\"The fidelity of the input quantum state is: {p:.5f}\")\n",
" print(f\"The fidelity of the purified quantum state is: {fidelity.numpy()[0]:.5f}\")\n",
" print(f\"The probability of successful purification is: {suc_rate.numpy()[0]:#.3%}\")\n",
" \n",
" # Print the output state\n",
" rho_out = status_fin[0].state.numpy()\n",
" print(\"========================================================\")\n",
" print(f\"The output state is:\\n {np.around(rho_out,4)}\")\n",
" print(f\"The initial negativity is: {negativity(bell_diagonal_state(p, (1-p)/2, (1-p)/3, (1-p)/6))}\")\n",
" print(f\"The final negativity is: {negativity(rho_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One can observe that the simulation result is in exact accordance with the theoretical values."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"The DEJMPS protocol can effectively extract one entangled pair with higher fidelity from two noisy pairs. And compared to the BBPSSW protocol [2], it can be applied to Bell-diagonal states instead of isotropic states. Note that isotropic state is a special case of Bell-diagonal state. So in this sense, the DEJMPS protocol is more general than the BBPSSW protocol. However, it also shares the same disadvantages of the BBPSSW protocol including the limited type of input states and poor scalability. \n",
"\n",
"Next, We suggest interested readers to check the tutorial on [how to design a new distillation protocol with LOCCNet](./EntanglementDistillation_LOCCNet_EN.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## References\n",
"\n",
"[1] Deutsch, David, et al. \"Quantum privacy amplification and the security of quantum cryptography over noisy channels.\" [Physical Review Letters 77.13 (1996): 2818.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.2818)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 纠缠蒸馏 -- LOCCNet 设计协议\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概述\n",
"\n",
"量子纠缠在量子通信,量子计算和其他许多量子技术中起着至关重要的作用。因此,如果我们想在这些领域构建实际应用,探测、传输和分发量子纠缠是必不可少的任务。但是在实际操作中,误差是不可避免的。这些误差可能来自于生产纠缠设备的缺陷,也可能是传输量子纠缠时的信道噪声。随着传输距离的增加,噪声会导致纠缠资源的不断减少。而纠缠蒸馏(entanglement distillation)这项技术的开发目的正是为了补偿各种噪声引起的纠缠资源消耗。基本的实现原理为通过操作多对含噪的纠缠态将纠缠资源集中在其中一对纠缠态上,并使之尽可能的接近**最大纠缠态**(qubit 情况下,通常是大家熟知的贝尔态)。从这个意义上讲,也可以将纠缠蒸馏看作一种提纯/纠错协议。此过程通常由 Alice 和 Bob 两个远程参与方执行,由于双方在空间上会相隔一定的距离,因此仅允许本地操作和经典通信(LOCC)[1]。纠缠蒸馏的概念最早由 Bennett 等人于 1996 年提出 [2],最初的协议被称为 **BBPSSW 协议**。后来 Deutsch 等人提出了一个新的蒸馏协议,即 DEJMPS 协议 [3]。\n",
"\n",
"但是,BBPSSW 协议和 DEJMPS 协议都是针对特定噪声态设计的,比如:BBPSSW 是针对 isotropic 态设计而 DEJMPS 协议是针对贝尔对角态(Bell-diagonal state)设计的。实际上,设计出一个通用的能够应对所有噪声进行提纯的协议几乎是不可能的。同时,由于 LOCC 协议结构的复杂性,每次遇到新种类的噪声时用纸和笔重新设计一个蒸馏协议是非常困难的。LOCCNet 作为一个设计 LOCC 协议的机器学习框架,就是为了解决上述问题而存在的。在 LOCCNet 的加持下,只要给定纠缠态中噪声的数学形式,设计对应的蒸馏方案就会变成一件十分简单的事情。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 预备知识\n",
"\n",
"在谈论纠缠蒸馏的时候,我们通常使用蒸馏后的输出态 $\\rho_{out}$ 和贝尔态 $|\\Phi^+\\rangle$ 之间的**保真度** $F$ 来量化纠缠蒸馏协议的好坏,保真度 $F$ 定义为\n",
"\n",
"$$\n",
"F(\\rho_{out}, \\Phi^+) \\equiv \\langle \\Phi^+|\\rho_{out}|\\Phi^+\\rangle.\n",
"\\tag{1}\n",
"$$\n",
"\n",
"**注意:** 通常情况下,LOCC 纠缠蒸馏协议并不是在所有测量结果下都算成功,一般记成功概率为 $p_{succ}$。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 协议的设计逻辑\n",
"\n",
"在本教程中,我们用4份相同的 **Isotropic 态** (也被称为 Werner 态)蒸馏出一份具有更高保真度输出态(相较于初始态,输出态更接近贝尔态 $|\\Phi^+\\rangle$)。这种协议被称为 $4\\rightarrow 1$ LOCC 蒸馏协议。相对应的,BBPSSW 和 DEJMPS 协议属于 $2\\rightarrow 1$ LOCC 蒸馏协议(因为一开始使用了2份相同的初始态)。\n",
"Isotropic 态是由 $|\\Phi^+\\rangle$ 和完全混合态(白噪声)$I/4$ 组成,\n",
"\n",
"$$\n",
"\\rho_{\\text{iso}}(p) = p\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + (1-p)\\frac{I}{4}, \\quad p \\in [0,1]\n",
"\\tag{2}\n",
"$$\n",
"\n",
"在我们的例子中 $p=0.7$,于是输入态就是\n",
"\n",
"$$\n",
"\\rho_{in} = \\rho_{\\text{iso}}(0.7)= 0.7\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + 0.075 I.\n",
"\\tag{3}\n",
"$$\n",
"\n",
"为了通过 LOCC 完成纠缠蒸馏,我们需要两位身处于两个地方的参与者 $A$ (Alice) 和 $B$ (Bob)。在最开始的时候,他们共享四份纠缠量子比特对 $\\rho_{A_0B_0}, \\rho_{A_1B_1}, \\rho_{A_2B_2}$ 和 $\\rho_{A_3B_3}$,每一份的初始态都为 $\\rho_{in} = \\rho_{\\text{iso}}(p =0.7)$。如此,Alice 手中有四个量子比特,分别是 $A_0, A_1, A_2, A_3$;对应地,Bob 手中也有四个量子比特,$B_0, B_1, B_2, B_3$。完成上述初始化后,Alice 和 Bob 需要选择通讯的轮数(communication round)$r$,即他们需要交换经典信息(可以是各自的测量结果)多少次。为了方便讨论,这里我们选择通讯轮数为 1。接下来, Alice 和 Bob 需要进行下述操作使得 LOCCNet 学习出最优的本地操作(Local Operation)。更准确的说法是学习出代表本地操作的量子神经网络(Quantum Neural Network, QNN)的最优参数。\n",
"\n",
"1. 设计一个 QNN 架构 $U(\\boldsymbol\\theta)$ 如图 1所示,其中 $R(\\theta)$ 表示单比特通用门。我们可以通过调用 Paddle Quantum 中的 `u3(theta, phi, lam, which_qubit)` 来实现该旋转门。\n",
"2. 在量子比特通过步骤 1. 中设计的 QNN 电路后,Alice 和 Bob 需要对除去 $A_0$ and $B_0$ 的剩余量子比特进行测量。测量结果 $M = \\{m_{A_1}m_{B_1}, m_{A_2}m_{B_2}, m_{A_3}m_{B_3}\\}$ 需要通过经典方式告知对方。\n",
"3. 如果每对的测量结果都相同,即 $m_{A_1}m_{B_1}, m_{A_2}m_{B_2}, m_{A_3}m_{B_3}$ 的结果要么为 00, 要么为 11。这种情况下,我们称蒸馏成功,然后剩余的一对量子比特 $A_0B_0$ 作为输出态被保存,标记为 $\\rho_{AB}'$。如果测量结果不满足上述判定结果,则蒸馏失败,需要丢弃量子比特 $A_0B_0$。\n",
"4. 这里,我们在所以蒸馏成功的情况下定义一个累积的损失函数 $L = \\sum_{m_{A_j}m_{B_j}\\in \\{00,11\\}} \\big(1- \\text{Tr}(\\rho_{tar}\\rho_{AB}')\\big)$,其中 $\\text{Tr}(\\rho_{tar}\\rho_{AB}')$ 表示当前态 $\\rho_{AB}'$ 和目标态 $\\rho_{tar}=\\lvert\\Phi^+\\rangle \\langle \\Phi^+\\rvert$ 之间的态重叠。\n",
"5. 使用梯度下降的优化方案来更新 QNN 中的参数使得损失函数最小化。\n",
"6. 重复步骤 1-5 直至损失函数收敛。\n",
"7. 输出蒸馏后的态 $\\rho_{out} = \\rho'_{A_0B_0}$。\n",
"\n",
"<center><img src=\"figures/distillation-fig-LOCCNet4.png\" height=\"200\" width=\"400\"></center>\n",
"<div style=\"text-align:center\">图 1: 由 LOCCNet 设计的纠缠蒸馏方案示意图 </div>\n",
"\n",
"**注释:** 图 1中的 QNN 架构设计仅是一个示意图,并不固定。感兴趣的读者可以设计一个自己的 QNN 架构。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Paddle Quantum 代码实现\n",
"\n",
"首先,我们需要导入所有依赖包:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T07:03:59.029140Z",
"start_time": "2021-01-27T07:03:52.520296Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.locc import LoccAnsatz, LoccNet, LoccParty, LoccStatus\n",
"from paddle_quantum.state import isotropic_state, bell_state\n",
"from paddle_quantum.utils import logarithmic_negativity"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"然后,我们需要定义 QNN 和 损失函数"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T07:03:59.082475Z",
"start_time": "2021-01-27T07:03:59.036365Z"
}
},
"outputs": [],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # 添加第一个参与方 Alice\n",
" # 第一个参数 4 代表着 Alice 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(4, party_name=\"Alice\")\n",
" \n",
" # 添加第二个参与方 Bob\n",
" self.add_new_party(4, party_name=\"Bob\")\n",
" \n",
" # Alice 的参数\n",
" self.theta_1 = self.create_parameter(shape=[8, 3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi), dtype=\"float64\")\n",
" \n",
" # Bob 的参数\n",
" self.theta_2 = self.create_parameter(shape=[8, 3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi), dtype=\"float64\")\n",
" \n",
" # 生成输入态 isotropic state\n",
" _state = fluid.dygraph.to_variable(isotropic_state(2, 0.7))\n",
" \n",
" # 初始化量子态\n",
" # ('Alice', 0) 代表 Alice 的第一个量子比特 A0\n",
" # ('Bob', 0) 代表 Bob 的第一个量子比特 B0\n",
" self.set_init_status(_state, [[\"Alice\", 0], [\"Bob\", 0]])\n",
" self.set_init_status(_state, [[\"Alice\", 1], [\"Bob\", 1]])\n",
" self.set_init_status(_state, [[\"Alice\", 2], [\"Bob\", 2]])\n",
" self.set_init_status(_state, [[\"Alice\", 3], [\"Bob\", 3]])\n",
"\n",
" def QNN(self, cir, theta):\n",
" '''\n",
" 定义图 1 中的 QNN\n",
" '''\n",
" cir.u3(*theta[0], 0)\n",
" cir.u3(*theta[1], 1)\n",
" cir.u3(*theta[2], 2)\n",
" cir.u3(*theta[3], 3)\n",
" cir.cnot([0, 1])\n",
" cir.cnot([1, 2])\n",
" cir.cnot([2, 3])\n",
" cir.cnot([3, 0])\n",
" cir.u3(*theta[4], 0)\n",
" cir.u3(*theta[5], 1)\n",
" cir.u3(*theta[6], 2)\n",
" cir.u3(*theta[7], 3)\n",
"\n",
" def New_Protocol(self):\n",
" status = self.init_status\n",
"\n",
" # 创建 Alice 的局部操作\n",
" cir1 = self.create_ansatz(\"Alice\")\n",
" self.QNN(cir1, self.theta_1)\n",
"\n",
" # 创建 Bob 的局部操作\n",
" cir2 = self.create_ansatz(\"Bob\")\n",
" self.QNN(cir2, self.theta_2)\n",
"\n",
" # 运行 Alice 的电路\n",
" status = cir1.run(status)\n",
" \n",
" # 运行 Bob 的电路\n",
" status = cir2.run(status)\n",
" \n",
" # 测量量子比特,[\"000000\", \"000011\",\"001100\",\"110000\",\"001111\",\"111100\",\"110011\",\"111111\"] 代表蒸馏成功的情况\n",
" status1 = self.measure(status, [[\"Alice\", 1], [\"Bob\", 1],[\"Alice\", 2], [\"Bob\", 2], [\"Alice\", 3], [\"Bob\", 3]], \n",
" [\"000000\", \"000011\", \"001100\", \"110000\", \"001111\", \"111100\", \"110011\", \"111111\"])\n",
" \n",
" # 取偏迹除去测量后的量子比特,只留下 A0&B0 \n",
" status_fin = self.partial_state(status1, [[\"Alice\", 0], [\"Bob\", 0]])\n",
" target_state = fluid.dygraph.to_variable(bell_state(2))\n",
" \n",
" # 计算损失函数\n",
" loss = 0\n",
" for idx in range(0, len(status_fin)):\n",
" loss += 1 - trace(matmul(target_state, status_fin[idx].state)).real[0]\n",
" return loss, status_fin\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"最后,通过基于梯度的优化方法使得损失函数最小化。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T07:12:19.023140Z",
"start_time": "2021-01-27T07:03:59.103836Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"itr 0: 5.974937567506047\n",
"itr 10: 1.3569325419999818\n",
"itr 20: 1.1205194635673945\n",
"itr 30: 0.951684388970055\n",
"itr 40: 0.5876979234625004\n",
"itr 50: 0.5372695244330837\n",
"itr 60: 0.5199700730199454\n",
"itr 70: 0.5110181973259903\n",
"itr 80: 0.5064717635676458\n",
"itr 90: 0.5051218758007482\n",
"输入态的保真度为:0.77500\n",
"输出态的保真度为:0.93688\n",
"提纯成功率为:0.38654\n",
"========================================================\n",
"输出态为:\n",
" [[ 4.790e-01+0.j 3.000e-04+0.0003j -3.000e-04+0.0003j\n",
" 4.579e-01+0.0004j]\n",
" [ 3.000e-04-0.0003j 2.100e-02-0.j -0.000e+00+0.j\n",
" 3.000e-04-0.0003j]\n",
" [-3.000e-04-0.0003j -0.000e+00-0.j 2.100e-02+0.j\n",
" -3.000e-04-0.0003j]\n",
" [ 4.579e-01-0.0004j 3.000e-04+0.0003j -3.000e-04+0.0003j\n",
" 4.790e-01+0.j ]]\n",
"初始态的 logarithmic negativity 为: 0.6322682154995127\n",
"输出态的 logarithmic negativity 为: 0.9059439306384385\n"
]
}
],
"source": [
"ITR = 100 # 循环次数\n",
"LR = 0.2 # 学习率\n",
"\n",
"# 开启动态图模式\n",
"with fluid.dygraph.guard(fluid.CPUPlace()):\n",
" net = LOCC()\n",
"\n",
" # 选择 Adam 优化器\n",
" opt = fluid.optimizer.AdamOptimizer(learning_rate=LR, parameter_list=net.parameters())\n",
" loss_list = [] \n",
" \n",
" # 优化循环\n",
" for itr in range(ITR):\n",
" loss, status_fin = net.New_Protocol()\n",
" # 反向传播\n",
" loss.backward() \n",
" opt.minimize(loss)\n",
" # 清除梯度\n",
" net.clear_gradients() \n",
" loss_list.append(loss.numpy()[0])\n",
" \n",
" # 打印训练结果\n",
" if itr % 10 == 0:\n",
" print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
"\n",
" # 计算输入态的保真度\n",
" fidelity_in = (3 * 0.7 + 1) / 4\n",
" # 计算输出态的保真度\n",
" fidelity = (len(status_fin) - loss) / len(status_fin)\n",
" # 计算成功率\n",
" suc_rate = sum([s.prob for s in status_fin])\n",
"\n",
" print(\"输入态的保真度为:%.5f\" % fidelity_in)\n",
" print(\"输出态的保真度为:%.5f\" % fidelity.numpy()[0])\n",
" print(\"提纯成功率为:%.5f\" % suc_rate.numpy()[0])\n",
" rho_out = status_fin[0].state.numpy()\n",
" print(\"========================================================\")\n",
" print(f\"输出态为:\\n {np.around(rho_out, 4)}\")\n",
" print(f\"初始态的 logarithmic negativity 为: {logarithmic_negativity(isotropic_state(2, 0.7))}\")\n",
" print(f\"输出态的 logarithmic negativity 为: {logarithmic_negativity(rho_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 结论\n",
"\n",
"如同数值结果所示,由 LOCCNet 设计出新型蒸馏协议可以把4份保真度为0.775的 isotropic 态蒸馏出1份保真度为0.937的两比特量子态。在相同条件下,扩展版的 DEJMPS 协议 [3] 只能够提纯出保真度为 0.924 的量子态,低于新的蒸馏协议。除了能够获得高保真度外,我们框架有着更广的适用范围和良好的可扩展性。对蒸馏的感兴趣的读者可以尝试多轮通讯会对蒸馏结果产生什么样的结果。当然,我们也欢迎读者用该框架来蒸馏含有不同噪声的态。\n",
"\n",
"LOCCNet 有着广泛的应用,纠缠蒸馏仅仅是其中的一小部分。此外,我们想要强调的是通过 LOCCNet 训练出来的协议是可以在近期量子设备上实现从而进行验证的。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## 参考文献\n",
"\n",
"[1] Chitambar, Eric, et al. \"Everything you always wanted to know about LOCC (but were afraid to ask).\" [Communications in Mathematical Physics 328.1 (2014): 303-326.](https://link.springer.com/article/10.1007/s00220-014-1953-9)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[3] Deutsch, David, et al. \"Quantum privacy amplification and the security of quantum cryptography over noisy channels.\" [Physical Review Letters 77.13 (1996): 2818.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.2818)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entanglement Distillation -- Protocol Design with LOCCNet\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"Quantum entanglement plays a vital role in quantum communication, quantum computing, and many other quantum technologies. Therefore, detecting, transmitting, and distributing quantum entanglement reliably are essential tasks if we want to build real applications in those fields. However, errors are inevitable in the real world. They could come from imperfect equipment when we create entanglement (preparation errors), or the quantum channel used to transmit entanglement is noisy, and we gradually lose the degree of entanglement as the transmission distance increases. The aim of entanglement distillation is to compensate for those losses and restore a **maximally entangled state** at the cost of many noisy entangled states. In this sense, one could also refer entanglement distillation as a purification/error-correction protocol. This process often involves two remote parties Alice and Bob such that only Local Operations and Classical Communication (LOCC) are allowed [1]. Many distillation protocols have been proposed since 1996, including the famous BBPSSW [2] and the DEJMPS protocol [3]. \n",
"\n",
"However, the BBPSSW and DEJMPS distillation protocols are designed for specific types of noisy states (i.e., isotropic states and Bell-diagonal states, respectively). It is nearly impossible to find a single distillation protocol that could purify all kinds of noises. Due to the complicated mathematical structure of LOCC, designing a new distillation protocol is time-consuming with paper and pencil only. LOCCNet, a machine learning framework for LOCC protocols, is designed to reduce the effort as much as possible. With LOCCNet, it will be easier to design new distillation protocols as long as we can characterize the mathematical form of noise introduced to the entanglement resources. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Preliminary\n",
"\n",
"In the context of entanglement distillation, we usually use the **state fidelity** $F$ between the distilled state $\\rho_{out}$ and the maximally entangled Bell state $|\\Phi^+\\rangle$ to quantify the performance of a distillation protocol, where\n",
"\n",
"$$\n",
"F(\\rho_{out}, \\Phi^+) \\equiv \\langle \\Phi^+|\\rho_{out}|\\Phi^+\\rangle.\n",
"\\tag{1}\n",
"$$\n",
"\n",
"**Note:** In general, LOCC distillation protocols are probabilistic, and hence we are also interested in the success rate $p_{succ}$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Protocol design logic\n",
"\n",
"In this tutorial, we will go through an example that distills four identical **isotropic states** (or Werner state) $\\rho_{in}= \\rho_{\\text{iso}}$ into a single final state $\\rho_{out}$ with a higher state fidelity $F$ (closer to the Bell state $|\\Phi^+\\rangle$). We call this type of protocol as the $4\\rightarrow 1$ LOCC distillation class, while the original BBPSSW and DEJMPS protocols belong to the $2\\rightarrow 1$ LOCC distillation class. The isotropic state is a parametrized family of mixed states consist of $|\\Phi^+\\rangle$ and the completely mixed state (white noise) $I/4$,\n",
"\n",
"$$\n",
"\\rho_{\\text{iso}}(p) = p\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + (1-p)\\frac{I}{4}, \\quad p \\in [0,1]\n",
"\\tag{2}\n",
"$$\n",
"\n",
"In our example, we set $p=0.7$ and the input state becomes:\n",
"\n",
"$$\n",
"\\rho_{in} = \\rho_{\\text{iso}}(0.7)= 0.7\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + 0.075 I.\n",
"\\tag{3}\n",
"$$\n",
"\n",
"\n",
"To fulfill the task of distillation through LOCC, we introduce two remote parties, $A$ (Alice) and $B$ (Bob). At the very beginning, they share four copies of entangled qubit pairs $\\rho_{A_0B_0}, \\rho_{A_1B_1}, \\rho_{A_2B_2}$ and $\\rho_{A_3B_3}$. Each copy is initialized as $\\rho_{in} = \\rho_{\\text{iso}}(p =0.7)$. Alice holds four qubits $A_0, A_1, A_2, A_3$ in one place and Bob holds $B_0, B_1, B_2, B_3$ in another place. With these initial setups, Alice and Bob could choose the communication rounds $r$, which indicates how many times they would measure their subsystems and transmit classical data with each other. For simplicity, it is natural to start with $r=1$. Then, Alice and Bob can use LOCCNet to find out the correct local operations (encoded as quantum neural networks, QNNs) before communication by following the steps below:\n",
"\n",
"1. Design a general QNN architecture $U(\\boldsymbol\\theta)$ as shown in Figure 1, where each $R(\\theta)$ represents a general rotation operation on the Bloch sphere. They are referred as the `u3(theta, phi, lam, which_qubit)` gate in Paddle Quantum.\n",
"2. After implementing the QNN, Alice and Bob measure all of the qubits except $A_0$ and $B_0$ in the computational basis. Then the measurement results $M = \\{m_{A_1}m_{B_1}, m_{A_2}m_{B_2}, m_{A_3}m_{B_3}\\}$ will be exchanged through a classical channel.\n",
"3. If the measurement results of all qubit pairs are the same (each pair $m_{A_1}m_{B_1}, m_{A_2}m_{B_2}, m_{A_3}m_{B_3}$ is either 00 or 11), the distillation is successful, and the pair $A_0B_0$ is reserved as the memory qubit pair to store the purified entanglement; If the measurement results are different, the protocol fails. Discard the quantum pair $A_0B_0$. At this point, we obtain the purified quantum pair $A_0B_0$ probabilistically, and its state is denoted as $\\rho_{AB}'$.\n",
"4. Define the accumulated loss function $L = \\sum_{m_{A_j}m_{B_j}\\in \\{00,11\\}} \\big(1- \\text{Tr}(\\rho_{tar}\\rho_{AB}')\\big)$ over the successful distillations, where $\\text{Tr}(\\rho_{tar}\\rho_{AB}')$ is the state overlap between the current state $\\rho_{AB}'$ and the target state $\\rho_{tar}=\\lvert\\Phi^+\\rangle \\langle \\Phi^+\\rvert$.\n",
"5. Use gradient-based optimization methods to update parameters in QNN and minimize the loss function.\n",
"6. Repeat steps 1-5 until the loss function converges.\n",
"7. Output the purified state $\\rho_{out} = \\rho'_{A_0B_0}$.\n",
"\n",
"**Note:** The QNN structure used is merely an illustration. Readers are welcomed to try their own designs.\n",
"\n",
"\n",
"<center><img src=\"figures/distillation-fig-LOCCNet4.png\" height=\"200\" width=\"400\"></center>\n",
"<div style=\"text-align:center\">Figure 1: Schematic diagram of a distillation protocol designed with LOCCNet. </div>\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulation with Paddle Quantum\n",
"\n",
"First, we import all the dependencies:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T04:17:11.026674Z",
"start_time": "2021-01-27T04:17:04.505712Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.locc import LoccAnsatz, LoccNet, LoccParty, LoccStatus\n",
"from paddle_quantum.state import isotropic_state, bell_state\n",
"from paddle_quantum.utils import logarithmic_negativity"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Secondly, we define the QNN and loss function"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T04:17:11.074048Z",
"start_time": "2021-01-27T04:17:11.030252Z"
}
},
"outputs": [],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # Add the first party Alice \n",
" # The first parameter 4 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(4, party_name=\"Alice\")\n",
" \n",
" # Add the second party Bob\n",
" # The first parameter 4 stands for how many qubits B holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(4, party_name=\"Bob\")\n",
" \n",
" # Alice‘s params\n",
" self.theta_1 = self.create_parameter(shape=[8, 3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi), dtype=\"float64\")\n",
" \n",
" # Bob's params\n",
" self.theta_2 = self.create_parameter(shape=[8, 3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi), dtype=\"float64\")\n",
" \n",
" # Generate input isotropic states\n",
" _state = fluid.dygraph.to_variable(isotropic_state(2, 0.7))\n",
" \n",
" # Distribute the pre-shared entangled states\n",
" self.set_init_status(_state, [[\"Alice\", 0], [\"Bob\", 0]])\n",
" self.set_init_status(_state, [[\"Alice\", 1], [\"Bob\", 1]])\n",
" self.set_init_status(_state, [[\"Alice\", 2], [\"Bob\", 2]])\n",
" self.set_init_status(_state, [[\"Alice\", 3], [\"Bob\", 3]])\n",
"\n",
" def QNN(self, cir, theta):\n",
" '''\n",
" Define the QNN illustrated in Figure 1\n",
" '''\n",
" cir.u3(*theta[0], 0)\n",
" cir.u3(*theta[1], 1)\n",
" cir.u3(*theta[2], 2)\n",
" cir.u3(*theta[3], 3)\n",
" cir.cnot([0, 1])\n",
" cir.cnot([1, 2])\n",
" cir.cnot([2, 3])\n",
" cir.cnot([3, 0])\n",
" cir.u3(*theta[4], 0)\n",
" cir.u3(*theta[5], 1)\n",
" cir.u3(*theta[6], 2)\n",
" cir.u3(*theta[7], 3)\n",
"\n",
" def New_Protocol(self):\n",
" status = self.init_status\n",
"\n",
" # Create Alice's circuit\n",
" cir1 = self.create_ansatz(\"Alice\")\n",
" self.QNN(cir1, self.theta_1)\n",
"\n",
" # Create Bob's circuit\n",
" cir2 = self.create_ansatz(\"Bob\")\n",
" self.QNN(cir2, self.theta_2)\n",
"\n",
" # Execute Alice's circuit\n",
" status = cir1.run(status)\n",
" \n",
" # Execute Bob's circuit\n",
" status = cir2.run(status)\n",
" \n",
" # Measure qubits,[\"000000\", \"000011\",\"001100\",\"110000\",\"001111\",\"111100\",\"110011\",\"111111\"] represent successful cases\n",
" status1 = self.measure(status, [[\"Alice\", 1], [\"Bob\", 1],[\"Alice\", 2], [\"Bob\", 2], [\"Alice\", 3], [\"Bob\", 3]], \n",
" [\"000000\", \"000011\", \"001100\", \"110000\", \"001111\", \"111100\", \"110011\", \"111111\"])\n",
" \n",
" # Trace out all the qubits but A_0 and B_0\n",
" status_fin = self.partial_state(status1, [[\"Alice\", 0], [\"Bob\", 0]])\n",
" target_state = fluid.dygraph.to_variable(bell_state(2))\n",
" \n",
" # Calculate loss function\n",
" loss = 0\n",
" for idx in range(0, len(status_fin)):\n",
" loss += 1 - trace(matmul(target_state, status_fin[idx].state)).real[0]\n",
" return loss, status_fin\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lastly, minimize the loss function with gradient-based optimization methods."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T04:27:35.758702Z",
"start_time": "2021-01-27T04:17:11.098191Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"itr 0: 6.313622673221091\n",
"itr 10: 1.5322711051309412\n",
"itr 20: 0.7766710710147502\n",
"itr 30: 0.7114199957285438\n",
"itr 40: 0.6394957129763132\n",
"itr 50: 0.6085969124908581\n",
"itr 60: 0.5896473681859186\n",
"itr 70: 0.5234827632327423\n",
"itr 80: 0.5150627180761873\n",
"itr 90: 0.505887118309619\n",
"The fidelity of the input quantum state is:0.77500\n",
"The fidelity of the purified quantum state is: 0.93669\n",
"The probability of successful purification is:0.38664\n",
"========================================================\n",
"The output state is:\n",
" [[ 4.787e-01+0.j -3.000e-04-0.0003j 3.000e-04-0.0003j\n",
" 4.580e-01+0.0004j]\n",
" [-3.000e-04+0.0003j 2.130e-02-0.j 0.000e+00+0.j\n",
" -3.000e-04+0.0003j]\n",
" [ 3.000e-04+0.0003j 0.000e+00-0.j 2.130e-02+0.j\n",
" 3.000e-04+0.0003j]\n",
" [ 4.580e-01-0.0004j -3.000e-04-0.0003j 3.000e-04-0.0003j\n",
" 4.787e-01-0.j ]]\n",
"The initial logarithmic negativity is: 0.6322682154995127\n",
"The final logarithmic negativity is: 0.905643209528966\n"
]
}
],
"source": [
"ITR = 100 # Number of iterations\n",
"LR = 0.2 # Learning rate\n",
"\n",
"# Initialize the dynamic graph mode\n",
"with fluid.dygraph.guard(fluid.CPUPlace()):\n",
" net = LOCC()\n",
"\n",
" # Choose Adam optimizer\n",
" opt = fluid.optimizer.AdamOptimizer(learning_rate=LR, parameter_list=net.parameters())\n",
" loss_list = [] \n",
" \n",
" # Optimization loop\n",
" for itr in range(ITR):\n",
" loss, status_fin = net.New_Protocol()\n",
" # Backpropagation\n",
" loss.backward() \n",
" opt.minimize(loss)\n",
" # Clean gradients\n",
" net.clear_gradients() \n",
" loss_list.append(loss.numpy()[0])\n",
" \n",
" # Print training result\n",
" if itr % 10 == 0:\n",
" print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
"\n",
" # Calculate input state fidelity\n",
" fidelity_in = (3 * 0.7 + 1) / 4\n",
" # Calculate output state fidelity\n",
" fidelity = (len(status_fin) - loss) / len(status_fin)\n",
" # Calculate successful rate\n",
" suc_rate = sum([s.prob for s in status_fin])\n",
"\n",
" print(\"The fidelity of the input quantum state is:%.5f\" % fidelity_in)\n",
" print(\"The fidelity of the purified quantum state is: %.5f\" % fidelity.numpy()[0])\n",
" print(\"The probability of successful purification is:%.5f\" % suc_rate.numpy()[0])\n",
" rho_out = status_fin[0].state.numpy()\n",
" print(\"========================================================\")\n",
" print(f\"The output state is:\\n {np.around(rho_out, 4)}\")\n",
" print(f\"The initial logarithmic negativity is: {logarithmic_negativity(isotropic_state(2, 0.7))}\")\n",
" print(f\"The final logarithmic negativity is: {logarithmic_negativity(rho_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"\n",
"As we can see, this new distillation protocol can purify four copies of isotropic states, each with a fidelity of 0.775, into a single two-qubit state with a fidelity of 0.937, which outperforms the extended DEJMPS protocol [3] with a distillation fidelity of 0.924 under the same setting. At the same time, our framework also exhibits advantages in terms of flexibility and scalability. With the help of LOCCNet, one can try various combinations and see what's the effect of increasing the communication rounds $r$, adding non-identical noisy entanglement before distillation, and importing different noise types.\n",
"\n",
"LOCCNet has a wide range of applications, and distillation is merely one of them. We want to point out that the protocols trained by LOCCNet are hardware efficient since every operation is applicable in near term quantum devices, and we offer the possibility of customizing QNN architectures. Now, it's time to design and train your own LOCC protocol! "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## References\n",
"\n",
"[1] Chitambar, Eric, et al. \"Everything you always wanted to know about LOCC (but were afraid to ask).\" [Communications in Mathematical Physics 328.1 (2014): 303-326.](https://link.springer.com/article/10.1007/s00220-014-1953-9)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[3] Deutsch, David, et al. \"Quantum privacy amplification and the security of quantum cryptography over noisy channels.\" [Physical Review Letters 77.13 (1996): 2818.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.2818)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# LOCCNet:用于 LOCC 协议设计的机器学习框架 \n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概述\n",
"\n",
"量子纠缠在量子通信、量子计算以及其他量子技术中是一种很重要的资源。因此,能否在这些领域构建出实际的应用,很大程度上取决于我们能否有效地利用量子纠缠这一资源。在 NISQ (Noisy Intermediate-Scale Quantum)时代,通过量子网络实现两个节点之间直接通讯量子信息是一项艰巨的任务。所以在当前阶段,通过本地操作和经典通讯(LOCC)[1] 来完成特定任务,是比全局操作(Global Operation)更为有效的方式。所谓本地操作和经典通讯,是指几个空间上分离的参与者只能在自己的实验室中执行本地操作,然后通过经典通讯的方式传递他们经典信息(可以是测量结果)。然而,设计 LOCC 协议来进行纠缠操作以及分布式量子信息处理是非常具有挑战性的,因为 LOCC 的结构通常很复杂并且很难用数学方法描述。为了更好地探索如何在近期量子设备上利用量子纠缠资源以及从长远角度来看进行分布式量子信息处理,我们设计了 **LOCCNet**,一种用于 LOCC 协议设计的机器学习框架。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 什么是 LOCC?\n",
"\n",
"正如上面所描述的,LOCC 指代的是本地操作和经典通讯(Local Operations and Classical Communication),即一个多量子比特系统分配给位于不同位置的多个实验室(参与方)。假如有 $N$ 实验室,每个实验室只能做对他们手中的子系统 $k \\in [1,\\cdots,N]$ 做量子操作 $\\{\\mathcal{E}^{(k)}_j\\}_{j=0}^{r}$。这些实验室之间允许传输包括测量结果在内的经典信息。LOCC 协议通常是根据通讯的轮数 $r$ 和实验室的数量 $N$ 进行分类的,记为 LOCC$_r(N)$。比如量子隐形传态协议 [2] 就是一个一轮通讯两个参与方的协议 LOCC$_1(2)$,参与的两方通常命名为 Alice 和 Bob。这个协议的任务是把一个未知的量子态 $\\lvert\\psi\\rangle$ 从 Alice 传输给 Bob,图 1 所示的流程图具体阐述了如何实现这一任务。\n",
"\n",
"\n",
"<img src=\"figures/teleportation-fig-circuit.jpg\" width=\"52%\" align = \"left\"/></center> <center><img src=\"figures/LOCC-fig-controltree.png\" width=\"48%\" align = \"right\"/> &nbsp; \n",
"<div style=\"text-align:center\">图 1:量子隐形传态协议的电路图(左)和树状图(右),其中 Alice 的测量结果 $m_1, m_2 \\in \\{0,1\\}$ </div>\n",
"\n",
"在量子隐形传态中,只有 Alice 对自己的量子比特进行了测量,Bob 的所有本地操作均取决于 Alice 的测量结果 $m_1m_2 \\in \\{00,01,10,11\\}$。当 $ k ^\\text{th}$ 一方的测量结果($ m_1m_2 ... m_n $)控制着后面的本地操作时,我们称这类 LOCC 协议为 **Control-Type**。当 Alice 和 Bob 都对自己手中的量子比特进行测量时,协议就会变得复杂起来,因为他们可以选择合作并决定下一步做什么,这种协议被我们称之为 **Cooperation-Type**。比如图 2 中描述的纠缠蒸馏协议,关于该协议的详细讨论请参考教程 [BBPSSW 协议](./EntanglementDistillation_BBPSSW_CN.ipynb)。\n",
"\n",
"\n",
"<img src=\"figures/LOCC-fig-BBPSSW.png\" width=\"52%\" align = \"left\"/></center> <center><img src=\"figures/LOCC-fig-cooptree.png\" width=\"48%\" align = \"right\"/> &nbsp; \n",
"<div style=\"text-align:center\">图 2:BBPSSW 蒸馏协议是一种 Cooperation-Type LOCC 协议。左图是电路图,右图是树状图,$m_1^{(1)}, m_1^{(2)} \\in \\{0,1\\}$,$m_j^{(k)}$ 表示的是第 $k^\\text{th}$ 参与方的测量结果。当测量结果为 $m_1^{(1)}m_1^{(2)} \\in \\{01,10\\}$ 时,判定协议失败。特别地,这里的本地操作为 $\\mathcal{E}_{0}^{(1)} = \\mathcal{E}_{0}^{(2)} = \\text{CNOT}$ 和 $\\mathcal{E}_{1}^{(1)} = \\mathcal{E}_{1}^{(2)} = I$。</div>\n",
"\n",
" \n",
"这些协议看上去十分简单,但是当参与方增多而且通讯轮数变多时,想要找到每一个参与方的最优的本地操作就会变得十分困难。现在我们大致了解了为什么说设计一个 LOCC 协议是一项艰巨的任务。即使如此困难,仍有许多重要的 LOCC 协议被科学家提了出来,比如:纠缠蒸馏(Entanglement Distillation) [3-4],纠缠转换(Entanglement Swapping) [5] 等。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LOCCNet 的设计理念\n",
"\n",
"我们从机器学习解决量子多体问题 [6] 以及预测蛋白质折叠结构 [7] 受到启发,使用机器学习的方法从众多可能的结果中搜寻最优的 LOCC 协议。为了实现上述目标,我们利用量子神经网络(Quantum Neural Networks, QNN)表示每个本地操作 $\\mathcal{E}^{(k)}_j$,这也就意味着树状图中的每个节点都代表着一个量子神经网络(QNN),也可以称为参数化量子电路(Parameterized Quantum circuit, PQC)$U(\\boldsymbol \\theta)$。在 Paddle Quantum 中,我们提供多种 QNN 模板以减少用户的学习成本。在设置 QNN 之后,我们便可以规划如何测量和通讯。下面需要做的就是学习目标函数,通常情况下,我们把目标函数编码成损失函数 $L$。举个例子,在量子隐形传态协议中,我们的学习目标是最大化 Alice 想要传输的态 $|\\psi\\rangle$ 和 Bob 最终得到的态 $|\\phi\\rangle$ 之间的保真度,也就是说 $L \\equiv \\sum_{m_1m_2} \\big(1- F(|\\psi\\rangle, |\\phi\\rangle)\\big)$。根据所处理的任务不同,损失函数会有相应的变化。最后一步,使用经典的优化方法(主要是梯度下降)来训练 QNN 中的参数。优化完成后,我们就获得了一个近似最优的 LOCC 协议。从使用者的角度来说,LOCCNet 这样一个框架可以极大地减少设计 LOCC 协议所用的时间,而且得到的协议也是很容易被实验验证。\n",
"\n",
"\n",
"**注释:** 当前版本下,LOCCNet 仅支持密度矩阵形式。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 功能简介"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"这一部分,我们将解释 LOCCNet 的主要函数,让读者明白如何使用该框架。首先,我们展示一段伪代码:\n",
"\n",
"\n",
"```python\n",
"from paddle_quantum.locc import LoccNet\n",
"\n",
"class Net(LoccNet):\n",
" def __init__(self):\n",
" super(Net, self).__init__()\n",
" # Step 0: 初始化系统\n",
" # Step 1: 初始化 QNN 参数\n",
" # Step 2: 设置初始量子态\n",
" \n",
" def forward(self):\n",
" # Step 3: 定义并执行 QNNs\n",
" # Step 4: 定义 protocol 的具体过程\n",
" # Step 5: 计算损失函数\n",
" return loss, final_status\n",
"```\n",
"首先,我们需要创建一个类 `class Net(LoccNet)` 来储存量子系统,与此同时,这个类也继承了 `LoccNet` 中的函数。LOCC 协议的主体部分都是在这个类 `Net()` 中实现的,它包含两个函数:`__init__()` and `forward()`。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在 `__init__()` 函数中,我们需要初始化所有的参与方、量子态以及 QNN 的参数。\n",
"\n",
"- `self.add_new_party(qubits_number, party_name=None)` 是用于添加一个新的参与方的函数,第一个参数代表该参与方有几个量子比特;第二个参数是可选参数,代表着参与者的名字。在协议中,我们可以选择是用过名字来指定参与方,也可以选择用编号来指定。如果我们希望使用名字,那么只需要在 `add_new_party` 函数中给 `party_name` 命名;如果希望使用编号,那么我们就不用给第二个参数赋值,第一个参与方会自动编号为 0,每增加一个参与方,其编号都会加一,同时该函数会将所添加的 party 的 ID 返回,其值根据定义会是 `int` 或者 `str`。\n",
"\n",
"- `self.set_init_status(state, which_qubits)` 是用于设置协议的初始态的函数。第一个参数 `state` 是量子态,必须是密度矩阵的形式;第二个参数 `which_qubits` 是定位量子比特(哪一参与方的第几个量子比特,如 `(\"Alice\", 0)`)。需要说明的是,我们必须初始化所有的量子比特,否则程序将出现错误。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在 `forward()` 函数中,我们需要定义协议的流程。如果我们想要训练一个模型,那么需要定义损失函数,并设置为 `forward()` 的返回值,这样才能不断更新参数使得损失函数最小化。如果我们仅仅是想要验证某个协议的结果,我们就做上述的事情,只需要把协议的流程定义清除,就可以把我们感兴趣的值设为返回值。在 `forward()` 函数中,我们主要做两件事情--量子操作和测量,我们为他们提供了相应的函数:\n",
"\n",
"- `self.create_ansatz(party_id)` 是为某一参与方创建本地量子电路的函数。所以参数 `party_id` 用来指定参与方。举个例子 `cir1 = self.create_ansatz(\"Alice\")` 为 Alice 创建了电路。之后,我们可以在电路中添加不同的操作比如 X 门, CNOT 门等,也可以在添加门之后通过 `run()` 函数来运行电路,得到运行后的结果,如 `status_out = cir1.run(status)`。\n",
"\n",
"- `self.measure(status, which_qubits, results_desired, theta=None)` 是用来进行测量的函数。第一个参数 `status` 是我们想要测量的态;第二个参数 `which_qubits` 代表着要测量的是哪一个量子比特。如果我们想测量的是 Alice 手中第 0 个量子比特,那么就需要给第二个参数赋值 `(\"Alice\", 0)`。如果我们想要同时测量两个量子比特,比如 Alice 手中的第 0 个量子比特和 Bob 手中的第 1 个量子比特,那么这个参数需要设为 `[(\"Alice\", 0), (\"Bob\", 1)]`。第三个参数 `results_desired` 是我们希望测量的结果,它只可以为 `\"0\"`,`\"1\"`,或者 `[\"0\", \"1\"]`。第四个参数 `theta` 是用于含参测量,如果我们不希望做含参测量操作,那么就不用给它赋值。\n",
"\n",
"- `self.partial_state(status, which_qubits, is_desired=True)` 是用来得到部分量子态的函数。在纠缠蒸馏中,我们可能只有一部分量子态是我们想要的目标态。比如我们想要将 Alice 的第 0 个量子比特和 Bob 的第 0 个量子比特作为目标态,则我们可以通过 `status = self.partial_state(status, [(\"Alice\", 0), (\"Bob\", 0)])`来得到。\n",
"\n",
"- `self.reset_state(status, state, which_qubits)` 可以重置部分量子态。有时候我们可能不想使用某些已经测量过的量子态,想将它重置为新的量子态来继续进行 LOCC。因此我们也提供了该功能。\n",
"\n",
"- `LoccStatus`:在 `LoccNet` 中,最小的信息单元不是量子态,而是 `LoccStatus`。它包含了量子态,从初始态得到该量子态的概率,以及测量结果。有时候,我们想要得到多个量子态,也就是说我们希望的测量结果是多个,比如在 `self.measure()` 函数中,`results_desired` 设置为 `[\"0\", \"1\"]`。由此,我们能够得到两组 `LoccStatus`,这种情况下,我们的函数返回的是由 `LoccStatus` 组成的 `list`。值得一提的是,不论是 `LoccStatus`,还是由 `LoccStatus` 组成的 `list`,我们的函数几乎都可以对其进行正常执行。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 欢迎使用 LOCCNet!\n",
"\n",
"在介绍完上述的 LOCCNet 之后,我们建议您从下面的教程中开始学习如何使用 LOCCNet 框架:\n",
"\n",
"- [纠缠蒸馏 -- BBPSSW 协议](EntanglementDistillation_BBPSSW_CN.ipynb)\n",
"- [纠缠蒸馏 -- DEJMPS 协议](EntanglementDistillation_DEJMPS_CN.ipynb)\n",
"- [纠缠蒸馏 -- LOCCNet 设计协议](EntanglementDistillation_LOCCNet_CN.ipynb)\n",
"- [量子隐态传输](QuantumTeleportation_CN.ipynb)\n",
"- [量子态分辨](StateDiscrimination_CN.ipynb)\n",
"\n",
"\n",
"LOCCNet 框架所能做的远不止上述几个方向,我们希望您可以使用这个新框架去探索更多有趣的协议!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## 参考文献\n",
"\n",
"\n",
"[1] Chitambar, Eric, et al. \"Everything you always wanted to know about LOCC (but were afraid to ask).\" [Communications in Mathematical Physics 328.1 (2014): 303-326.](https://link.springer.com/article/10.1007/s00220-014-1953-9)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 70.13 (1993): 1895.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895)\n",
"\n",
"[3] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[4] Deutsch, David, et al. \"Quantum privacy amplification and the security of quantum cryptography over noisy channels.\" [Physical Review Letters 77.13 (1996): 2818.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.2818)\n",
"\n",
"[5] Jin, Rui-Bo, et al. \"Highly efficient entanglement swapping and teleportation at telecom wavelength.\" [Scientific reports 5.1 (2015): 1-7.](https://www.nature.com/articles/srep09333)\n",
"\n",
"[6] Carleo, Giuseppe, and Matthias Troyer. \"Solving the quantum many-body problem with artificial neural networks.\" [Science 355.6325 (2017): 602-606.](https://science.sciencemag.org/content/355/6325/602)\n",
"\n",
"[7] Senior, Andrew W., et al. \"Improved protein structure prediction using potentials from deep learning.\" [Nature 577.7792 (2020): 706-710.](https://www.nature.com/articles/s41586-019-1923-7)\n",
"\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# LOCCNet: A Machine Learning Framework for LOCC Protocols\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"Quantum entanglement is an essential physical resource for quantum communication, quantum computation, and many other quantum technologies. Therefore, the ability to manipulate quantum entanglement reliably is an essential task if we want to build real applications in those fields. In the Noisy Intermediate-Scale Quantum (NISQ) era, directly transferring quantum information between the communication nodes inside a quantum network is an arduous task. Hence, the most natural set of operations to manipulate entanglement at this stage is the so-called Local Operations and Classical Communication (LOCC) [1] instead of global operations. Under this setup, several spatially separated parties can only implement local operations in their own labs and later communicates their measurement results (classical information) through a classical channel. Still, it is very challenging to design LOCC protocols for entanglement manipulation and further distributed quantum information processing tasks since the structure of LOCC is in general complicated and hard to characterize mathematically. To better explore the possibilities of near-term entanglement manipulation and long-term quantum information processing, we introduce **LOCCNet**, a machine learning framework for LOCC protocol design. \n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is LOCC?\n",
"\n",
"As we explained above, LOCC stands for Local Operations and Classical Communication. It is also known as the \"distant lab\" paradigm, where a multipartite quantum system is distributed to some spatially separated labs. Suppose there are $N$ labs involved, and each lab is allowed to implement a sequence of quantum operations $\\{\\mathcal{E}^{(k)}_j\\}_{j=0}^{r}$ with respect to their own subsystems $k \\in [1,\\cdots,N]$. These labs are allowed to communicate any classical data including all the measurement results. A general LOCC protocol can be categorized according to the communication rounds $r$ applied and the number of distant labs involved, denoted as LOCC$_r(N)$, and pictorially described by a tree graph. For example, the famous quantum teleportation protocol [2] belongs to the 1-round LOCC$_1(2)$ family where only two parties are involved (Alice and Bob). The basic idea is to transfer an unknown quantum state $|\\psi\\rangle$ from Alice to Bob and the workflow is summarized in Figure 1,\n",
"\n",
"\n",
"<img src=\"figures/teleportation-fig-circuit.jpg\" width=\"52%\" align = \"left\"/></center> <center><img src=\"figures/LOCC-fig-controltree.png\" width=\"48%\" align = \"right\"/> &nbsp; \n",
"<div style=\"text-align:center\">Figure 1. Quantum teleportation as an LOCC protocol presented in a circuit diagram (left) and a tree graph (right), where $m_1, m_2 \\in \\{0,1\\}$. </div>\n",
"\n",
"In quantum teleportation, only Alice measures her qubits and Bob's local operations are completely decided by Alice's measurement results $m_1m_2 \\in \\{00,01,10,11\\}$. We call this type of LOCC protocols as **the control-type** when the measurement results from $k^\\text{th}$ party, a classical bit string $m_1m_2...m_n$, controls the subsequent local operations in each party ($k^\\text{th}$ party measures $n$ qubits). For simplicity, we would not discuss the case when there exists multiple controllers. Things could be very different when Alice and Bob both measure their subsystem. They can choose to cooperate with each other and decide what to do next. For example, the following **cooperation-type** LOCC protocol shown in Figure 2 is proposed for entanglement distillation. We refer all the details to another tutorial on [the BBPSSW protocol](./EntanglementDistillation_BBPSSW_EN.ipynb).\n",
"\n",
"\n",
"<img src=\"figures/LOCC-fig-BBPSSW.png\" width=\"52%\" align = \"left\"/></center> <center><img src=\"figures/LOCC-fig-cooptree.png\" width=\"48%\" align = \"right\"/> &nbsp; \n",
"<div style=\"text-align:center\">Figure 2. The BBPSSW distillation protocol as a cooperation-type LOCC protocol presented in a circuit diagram (left) and a tree graph (right), where $m_1^{(1)}, m_1^{(2)} \\in \\{0,1\\}$. The notation $m_j^{(k)}$ refers to the measurement result on qubit $j$ of the $k^\\text{th}$ party. The protocol fails when $m_1^{(1)}m_1^{(2)} \\in \\{01,10\\}$. Here, $\\mathcal{E}_{0}^{(1)} = \\mathcal{E}_{0}^{(2)} = \\text{CNOT}$ and $\\mathcal{E}_{1}^{(1)} = \\mathcal{E}_{1}^{(2)} = I$.</div>\n",
"\n",
" \n",
" \n",
"These protocols look quite simple, but things could become extremely complicated when many parties communicate with each other for multiple rounds and we still need to find out the best local operations each party should apply in each round. Now, we should at least have a taste of why designing an LOCC protocol is challenging. With such difficulty, many practical LOCC protocols have been purposed to fulfill meaningful tasks including entanglement distillation [3-4], entanglement swapping [5], and so on.\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Philosophy of LOCCNet\n",
"\n",
"Inspired by the success of Machine Learning (ML) in solving the quantum many-body problem [6] and protein folding structure prediction [7], we would like to adopt the learning capability of ML to help search optimal LOCC protocols among many possible combinations. The basic idea of LOCCNet is to utilize quantum neural networks (QNNs) to represent each local quantum operation $\\mathcal{E}^{(k)}_j$. That means each node represented in the tree graph of an LOCC protocol is now replaced by a QNN, which is essentially a parametrize quantum circuit (PQC) denoted by $U(\\boldsymbol \\theta)$. In Paddle Quantum, we already provide many QNN templates to reduce the learning cost for users. Once we set up the QNN, we can freely choose the measurement and communication plan. One last recipe we would need is a learning objective which is usually encoded as a loss function $L$. For example in quantum teleportation, the learning objective could be maximizing the state fidelity between Alice's state $|\\psi\\rangle$ and the state Bob receives $|\\phi\\rangle$ under four possible measurement results, meaning that $L \\equiv \\sum_{m_1m_2} \\big(1- F(|\\psi\\rangle, |\\phi\\rangle)\\big)$. The loss function should be designed according to the specific goal we want to accomplish. Finally, classical optimization methods (mainly gradient-based) will be applied to train the parameters in each QNN. Once the optimization has been done, we could obtain a near-optimal LOCC protocol. From our perspective, such a framework could sharply reduce the efforts to develop novel LOCC protocols, and the results should be easy to verify by experiments.\n",
"\n",
"\n",
"**Note:** LOCCNet only supports density matrix formulation at the current version."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Building blocks "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this section, we will introduce all the main building blocks of LOCCNet to give users a taste on what our framework can achieve. We firstly present a piece of pseudo code:\n",
"\n",
"```python\n",
"from paddle_quantum.locc import LoccNet\n",
"\n",
"class Net(LoccNet):\n",
" def __init__(self):\n",
" super(Net, self).__init__()\n",
" # Step 0: System initialization \n",
" # Step 1: QNN parameter initialization \n",
" # Step 2: Set up the initial quantum states\n",
"\n",
" def forward(self):\n",
" # Step 3: Define and execute QNNs\n",
" # Step 4: Measurement\n",
" # Step 5: Calculate the loss function\n",
" return loss, final_status\n",
"```\n",
"\n",
"Firstly, we need to create a python class through `class Net(LoccNet)` to reserve the complete quantum system and many helpful functions will be inherited from `LoccNet`. The main body of an LOCC protocol is realized in this customized class `Net()` which consists of two functions -- `__init__()` and `forward()`. In the `__init__()` function, you can initialize all the remote parties, quantum state in each subsystem, and the parameters of QNN.\n",
"\n",
"- `self.add_new_party(qubits_number, party_name=None)` is used to add one party, the first parameter means the number of qubits held by this party; the second parameter is optional, which stands for the party's name. In a protocol, you can choose to identify each party either by their name character ID or a number ID. If you want to use the mode of character, you need to specify `party_name` when calling `add_new_party`; if you choose the latter mode, you don't need to set them, and the numbers will increase from 0 in the order you add them.\n",
"\n",
"- `self.set_init_status(state, which_qubits)` is used to set the initial quantum state to be processed by your protocol. Here `state` is always in density matrix formulation, and `which_qubits` represents the identification of quantum states (belong to which qubits held by certain parties). It should be noted that you must give all the qubits to the initial quantum state through this function. Otherwise, the program will occur unexpected errors."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the `forward()` function, we can define the specific workflow of the protocol. If you want to train a model, you need to define the loss function as the return value of the function `forward()` which will be minimize by update the trainable parameters. If you just want to verify the result of a protocol, you don't need to define the loss function and can return the object of interests, such as the final quantum state, the probability to get reach that state, and so on. Generally, we mainly perform two operations in the function `forward()`, quantum operations and measurement. We also provide corresponding functions for these two operations:\n",
"\n",
"- `self.create_ansatz(party_id)` is to create the local quantum circuit of some party, hence you should specify the party through `party_id`. For example, `cir1 = self.create_ansatz(\"Alice\")`creates a circuit belonging to Alice, and we can manipulate Alice's qubits through this circuit, add X gates, add CNOT gates, and so on.\n",
"\n",
"- `self.measure(status, which_qubits, results_desired, theta=None)` is the measurement function we provided in `LoccNet`. The `status` is the `LoccStatus` you want to measure, which we will introduce below. `which_qubits` means the qubits you want to measure. If you want to measure Alice's 0th qubit, just assign `which_qubits` to `(\"Alice\", 0)`. If you want to measure Alice's 0th qubit and Bob's 1st qubit at the same time, you can assign `which_qubits` to `[(\"Alice\", 0), (\"Bob\", 1)]`. `results_desired` is the measurement result you want, it can only include `\"0\"`, `\"1\"`, or `[\"0\", \"1\"]`. `theta` means the parameter of parameterized measurement, which you don't need to provide if you want to measure without parameter.\n",
"\n",
"- `LoccStatus`: In `LoccNet`, the smallest information processing unit we use is not a quantum state, but something we call the `LoccStatus`. It includes quantum state, the probability from the initial situation to this state, and the measurement results in this process. Sometimes, the state you want to get is not single, which means there are many situations in this protocol that are considered successful. We can also meet this demand. In the function `self.measure()`,if the parameter `results_desired` you given is a list, which means you want to get several `LoccStatus`, it would return a list of `LoccStatus`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Welcome aboard!\n",
"\n",
"\n",
"After introducing all the necessary components of LOCCNet, we suggest starting with one of following tutorials and build a deeper understanding with coding:\n",
"\n",
"- [Entanglement Distillation -- the BBPSSW protocol](EntanglementDistillation_BBPSSW_EN.ipynb)\n",
"- [Entanglement Distillation -- the DEJMPS protocol](EntanglementDistillation_DEJMPS_EN.ipynb)\n",
"- [Entanglement Distillation -- Protocol design with LOCCNet](EntanglementDistillation_LOCCNet_EN.ipynb)\n",
"- [Quantum Teleportation](QuantumTeleportation_EN.ipynb)\n",
"- [Quantum State Discrimination](StateDiscrimination_EN.ipynb)\n",
"\n",
"\n",
"What LOCCNet can do is far more than the listed topics. We encourage you to explore more possibilities with this new framework!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## References\n",
"\n",
"\n",
"[1] Chitambar, Eric, et al. \"Everything you always wanted to know about LOCC (but were afraid to ask).\" [Communications in Mathematical Physics 328.1 (2014): 303-326.](https://link.springer.com/article/10.1007/s00220-014-1953-9)\n",
"\n",
"[2] Bennett, Charles H., et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 70.13 (1993): 1895.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895)\n",
"\n",
"[3] Bennett, Charles H., et al. \"Purification of noisy entanglement and faithful teleportation via noisy channels.\" [Physical Review Letters 76.5 (1996): 722.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.722)\n",
"\n",
"[4] Deutsch, David, et al. \"Quantum privacy amplification and the security of quantum cryptography over noisy channels.\" [Physical Review Letters 77.13 (1996): 2818.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.2818)\n",
"\n",
"[5] Jin, Rui-Bo, et al. \"Highly efficient entanglement swapping and teleportation at telecom wavelength.\" [Scientific reports 5.1 (2015): 1-7.](https://www.nature.com/articles/srep09333)\n",
"\n",
"[6] Carleo, Giuseppe, and Matthias Troyer. \"Solving the quantum many-body problem with artificial neural networks.\" [Science 355.6325 (2017): 602-606.](https://science.sciencemag.org/content/355/6325/602)\n",
"\n",
"[7] Senior, Andrew W., et al. \"Improved protein structure prediction using potentials from deep learning.\" [Nature 577.7792 (2020): 706-710.](https://www.nature.com/articles/s41586-019-1923-7)\n",
"\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 训练量子隐形传态协议\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概述\n",
"\n",
"量子隐形传态(Quantum Teleportation)是可以通过本地操作和经典通信(LOCC)协议完成的另一项重要任务,该协议借助提前制备好的纠缠资源在两个空间上分离的通信节点(仅允许经典信道)之间传输量子信息。在本教程中,我们将首先简要回顾一下量子隐形传态协议,并使用量桨进行模拟。然后,我们将介绍如何使用 LOCCNet 学习出一个量子隐形传态协议。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 量子隐形传态协议\n",
"\n",
"量子隐形传态协议最初由 C. H. Bennett 等人在 1993 年提出 [1],并在 1997 年通过基于光子的实验平台进行了验证 [2-3]。其主要工作流程如下图所示。传输过程中需要 2 个通信节点或参与方,即 Alice 和 Bob。简单起见,我们仅考虑传输一个单量子比特的量子态 $|\\psi\\rangle_C$,这样整个系统总共需要 3 个量子比特,包括提前共享给 Alice 和 Bob 的最大纠缠态 $|\\Phi^+\\rangle_{AB} = \\frac{1}{\\sqrt{2}}(|00\\rangle + |11\\rangle)$。 Alice 持有量子比特 A 和 C, Bob 持有量子比特 B。**注释:量子隐形传态协议仅传输量子信息,而非在物理上直接传输量子比特。**\n",
"\n",
"\n",
"<center><img src=\"figures/teleportation-fig-circuit.jpg\" height=\"400\" width=\"600\"></center>\n",
"<div style=\"text-align:center\">图 1:量子隐形传态: 传输量子态 $|\\psi\\rangle$ 从 Alice 到 Bob </div>\n",
"\n",
"\n",
"\n",
"**步骤 I:** 一开始,整个量子系统的状态可以描述为\n",
"\n",
"$$\n",
"\\lvert\\varphi_{0}\\rangle \n",
"= \\lvert\\psi\\rangle_{C}\\otimes \\lvert\\Phi^+\\rangle_{AB} \n",
"= \\frac{1}{\\sqrt{2}}\\big[\\alpha\\lvert0\\rangle(\\lvert00\\rangle + \\lvert11\\rangle)+\\beta\\lvert1\\rangle(\\lvert00\\rangle + \\lvert11\\rangle)\\big],\n",
"\\tag{1}\n",
"$$\n",
"\n",
" 其中 Alice 要传输的量子态是 $|\\psi\\rangle_C = \\alpha|0\\rangle_C + \\beta|1\\rangle_C$ 并且 $\\alpha, \\beta \\in \\mathbb{C}$。\n",
"\n",
"**步骤 II:** Alice 在她持有的两个量子比特上作用 CNOT 门,得到作用后的量子态为\n",
"\n",
"$$\n",
"|\\varphi_1\\rangle \n",
"= \\frac{1}{\\sqrt{2}}\\big[\\alpha\\lvert0\\rangle(\\lvert00\\rangle + \\lvert11\\rangle)+\\beta\\lvert1\\rangle(\\lvert10\\rangle + \\lvert01\\rangle)\\big],\n",
"\\tag{2}\n",
"$$\n",
"\n",
"**步骤 III:** 接着 Alice 在她持有的量子比特 $C$ 上作用 Hadamard 门,使得整个系统的状态变为 $|\\varphi_2\\rangle$\n",
"\n",
"$$\n",
"|\\varphi_2\\rangle = \\frac{1}{2}\\big[\\alpha(\\lvert0\\rangle + \\lvert1\\rangle)(\\lvert00\\rangle + \\lvert11\\rangle)+\\beta(\\lvert0\\rangle - \\lvert1\\rangle)(\\lvert10\\rangle + \\lvert01\\rangle)\\big],\n",
"\\tag{3}\n",
"$$\n",
"\n",
"为了更好地观察后续的测量结果,不妨将上述态重新写为\n",
"\n",
"$$\n",
"\\lvert\\varphi_{2}\\rangle = \\frac{1}{2}\\big[\\lvert00\\rangle(\\alpha\\lvert0\\rangle + \\beta\\lvert1\\rangle) + \\lvert01\\rangle(\\alpha\\lvert1\\rangle + \\beta\\lvert0\\rangle) + \\lvert10\\rangle(\\alpha\\lvert0\\rangle - \\beta\\lvert1\\rangle) + \\lvert11\\rangle(\\alpha\\lvert1\\rangle - \\beta\\lvert0\\rangle)\\big].\n",
"\\tag{4}\n",
"$$\n",
"\n",
"**步骤 IV:** Alice 在计算基(computational basis)$\\{|00\\rangle, |01\\rangle, |10\\rangle, |11\\rangle\\}$ 上测量她的两个量子比特并将结果 $m_1m_2$ 通过经典信道发送给 Bob 。 总共有 4 种不同的可能性: $m_1m_2 \\in \\{ 00, 01,10, 11\\}$。 然后, Bob 根据收到的消息在其量子比特 $B$ 上进行对应的操作。\n",
"\n",
"- 如果测量结果为 $m_1m_2 = 00$,则 Bob 的态将为 $\\alpha\\lvert0\\rangle + \\beta\\lvert1\\rangle$。 无需任何操作即可完成传输。\n",
"- 如果测量结果为 $ m_1m_2 = 01 $,则 Bob 的态将为 $\\alpha\\lvert1\\rangle + \\beta\\lvert0\\rangle$。 Bob 需要在其量子比特上作用 $ X $ 门。\n",
"- 如果测量结果为 $ m_1m_2 = 10 $,则 Bob 的态将为 $\\alpha\\lvert0\\rangle - \\beta\\lvert1\\rangle$。 Bob 需要在其量子比特上作用 $ Z $ 门。\n",
"- 如果测量结果为 $ m_1m_2 = 11 $,则 Bob 的态将为 $\\alpha\\lvert1\\rangle - \\beta\\lvert0\\rangle$。 Bob 需要在其量子比特上执行 $ X $ 门操作,然后执行 $ Z $ 门操作。\n",
"\n",
"在下一节中,我们将介绍如何使用量桨模拟量子隐形传态协议。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Paddle Quantum 代码实现\n",
"\n",
"首先,我们需要导入所有依赖包:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T13:42:51.662130Z",
"start_time": "2021-01-26T13:42:45.290573Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.utils import state_fidelity\n",
"from paddle_quantum.state import bell_state, isotropic_state, density_op_random"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"初始化整个量子系统,然后定义量子电路和隐形传态协议。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T13:42:51.859178Z",
"start_time": "2021-01-26T13:42:51.666289Z"
}
},
"outputs": [],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # 初始化 LOCCNet\n",
" self.parties = list()\n",
" \n",
" # 添加第一个参与方 Alice\n",
" # 第一个参数 2 代表着 Alice 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(2, party_name=\"Alice\")\n",
" \n",
" # 添加第二个参与方 Bob\n",
" # 第一个参数 1 代表着 Bob 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(1, party_name=\"Bob\")\n",
" \n",
"\n",
" # 准备一个贝尔态\n",
" _state = fluid.dygraph.to_variable(bell_state(2))\n",
"# _state = fluid.dygraph.to_variable(isotropic_state(2, 0.8))\n",
"\n",
" \n",
" # 随机制备传输用的纯态 (rank =1)\n",
" random_state = density_op_random(n=1, real_or_complex=2, rank=1)\n",
" self.state_C = fluid.dygraph.to_variable(random_state)\n",
" \n",
" # 通过分配上述制备好的量子态初始化整个量子系统\n",
" # 这里 (\"Alice\", 0) 即表示量子比特 C\n",
" # 这里 (\"Alice\", 1) 即表示量子比特 A\n",
" # 这里 (\"Bob\", 0) 即表示量子比特 B\n",
"# print('提前分配好的纠缠态为:\\n', _state.numpy())\n",
" self.set_init_status(self.state_C, [(\"Alice\", 0)])\n",
" self.set_init_status(_state, [(\"Alice\", 1), (\"Bob\", 0)])\n",
"\n",
"\n",
" def teleportation(self):\n",
" status = self.init_status\n",
" \n",
" # 设置 Alice 的本地操作\n",
" cirA = self.create_ansatz(\"Alice\")\n",
" cirA.cnot([0, 1])\n",
" cirA.h(0)\n",
" \n",
" # 运行上述电路\n",
" status = cirA.run(status)\n",
" \n",
" # Alice 在计算基上测量她所持有的两个量子比特 C 还有 A\n",
" # 得到并记录四种结果 00,01,10,11\n",
" status_A = self.measure(status, [(\"Alice\", 0), (\"Alice\", 1)], [\"00\", \"01\", \"10\", \"11\"])\n",
" \n",
" # 用于记录平均保真度\n",
" fid_list = []\n",
"\n",
" # Bob 根据 Alice 的测量结果选择不同的门作用在自己的量子比特上\n",
" for i, s in enumerate(status_A):\n",
" \n",
" # 判断语句根据 Alice 的测量结果,进行不同操作 \n",
" if status_A[i].measured_result == '00':\n",
" \n",
" # 创建 Bob 的本地操作\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" \n",
" # 执行电路\n",
" status_B = cirB.run(s) \n",
" \n",
" # 仅保留 Bob 的量子比特 B\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
"\n",
" # 计算初始态和传输后态之间的保真度\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" \n",
" # 以下操作类似\n",
" elif status_A[i].measured_result == '01':\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.x(0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" elif status_A[i].measured_result == '10':\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.z(0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" elif status_A[i].measured_result == '11':\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.x(0)\n",
" cirB.z(0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" fid_avg = sum(fid_list)\n",
" \n",
" return fid_avg"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"然后,我们随机生成 200 个量子纯态,并使用态保真度 $ F $ 来衡量传输协议好坏,其中\n",
"\n",
"$$\n",
"F(\\rho,\\sigma) \\equiv \\text{tr}\\big( \\sqrt{\\sqrt{\\rho}\\sigma \\sqrt{\\rho}} \\big)^2.\n",
"\\tag{5}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T13:51:41.271853Z",
"start_time": "2021-01-26T13:51:20.136017Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"平均保真度 = 1.0 , 标准差 = 6.429150606796583e-09\n"
]
}
],
"source": [
"with fluid.dygraph.guard():\n",
" \n",
" np.random.seed(999) # 固定生成传输态的随机种子\n",
" num_state = 200 # 设置随机态的生成数量\n",
" list_fid = [] # 用于记录保真度\n",
" \n",
" # 开始采样 \n",
" for idx in range(num_state):\n",
" list_fid.append(LOCC().teleportation())\n",
"\n",
" print('平均保真度 =', np.around(sum(list_fid)/len(list_fid), 4), ', 标准差 =', np.std(list_fid))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**注释:** 我们要指出的是,该协议的有效性取决于提前分配好的贝尔态的品质。 感兴趣的读者可以将纠缠态从 `bell_state(2)` 更改为 `isotropic_state(2, p)`,并测试观察在贝尔态中出现的量子噪声将如何影响该协议的性能。 其中 isotropic 态的定义如下,\n",
"\n",
"$$\n",
"\\rho_{\\text{iso}}(p) = p\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + (1-p)\\frac{I}{4}, \\quad p \\in [0,1]\n",
"\\tag{6}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 使用 LOCCNet 学习量子隐形传态\n",
"\n",
"### 训练一个自定义的 LOCC 协议\n",
"\n",
"一般的 LOCC 协议可以通过经典通信的回合数 $ r $ 来进行分类。 原始的量子隐形传态协议是单轮通讯协议($ r = 1 $)。 为简单起见,我们也将通信回合数限制为单轮。与原始的协议不同,我们将使用参数化量子电路(PQC)来把 Bob 作用在量子比特上的固定门 $U\\in\\{X,Z\\}$ 替换成布洛赫球面上的一个广义旋转门 $ U_3 $, 其定义为\n",
"\n",
"$$\n",
"U_3(\\theta, \\phi, \\varphi) =\n",
"\\begin{bmatrix}\n",
"\\cos(\\frac{\\theta}{2}) & -e^{i\\varphi}\\sin(\\frac{\\theta}{2})\\\\\n",
"e^{i\\phi}\\sin(\\frac{\\theta}{2}) & e^{i(\\phi+\\varphi)} \\cos(\\frac{\\theta}{2})\n",
"\\end{bmatrix}.\n",
"\\tag{7}\n",
"$$\n",
"\n",
"这将为我们带来更强大的搜索能力,来寻找更多使用场景下的 LOCC 协议。类似地,我们将 Alice 的本地操作更改为更通用的参数化量子电路,这里具体使用的是一个内置的两量子比特通用门 `universal_2_qubit_gate(theta, which_qubit=[0,1])` [4]。基于以上条件,我们训练一个自定义的 LOCC 协议流程如下:\n",
"\n",
"1. Alice 对她所持有的两个量子比特作用两量子比特通用门。\n",
"2. 然后 Alice 在计算基上测量她的两个量子比特,并通过经典信道与 Bob 交流。\n",
"3. 共计有 4 种可能的测量结果:$m_1m_2 \\in \\{00, 01, 10, 11\\}$。 Bob 需要根据这些测量结果采取不同的本地操作。在 Bob 进行操作后,记其量子态为 $\\lvert \\psi\\rangle_{B}$。\n",
"4. 计算 $\\lvert \\psi\\rangle_{B}$ 与 $\\lvert\\psi\\rangle_C$(纯态)之间的量子态重叠(state overlap)并记为 $ O $。 由于 LOCCNet 框架目前仅支持密度矩阵形式,因此我们必须将它们重写为 $\\rho_{B} = |\\psi\\rangle\\langle\\psi|_B$ 和 $\\rho_{C} = |\\psi\\rangle\\langle\\psi|_C$。然后可以得到 $O = \\text{Tr}(\\rho_C\\rho_{B})$。对于纯态,此距离度量就是保真度 (fidelity)。\n",
"5. 将损失函数设置为 4 种可能测量结果的累加,即 $L = \\sum_{m_1m_2} \\big(1-\\text{Tr}(\\rho_C\\rho_{B})\\big)$,并使用基于梯度的优化方法更新 Alice 和 Bob 本地操作中的参数,从而使得损失函数最小化。\n",
"6. 重复步骤 1-5,直到损失函数收敛。\n",
"7. 生成一组随机的态 $\\{\\lvert\\psi_C\\rangle\\}$,并以平均保真度对训练出的传输协议进行基准测试。\n",
"\n",
"\n",
"\n",
"<center><img src=\"figures/teleportation-fig-LOCCNet.png\" height=\"400\" width=\"600\"></center>\n",
"<div style=\"text-align:center\">图 2: 用 LOCCNet 学习量子隐形传态协议 </div>\n",
"\n",
"\n",
"\n",
"**注释:** 为了确保训练出的 LOCC 协议对所有态均有效,我们将训练集设为 4 个线性独立态,即 $\\{|0\\rangle\\langle 0|,|1\\rangle\\langle 1|,|+\\rangle\\langle +|,|+\\rangle\\langle +|_y\\}$ 。其中 $|+\\rangle, |+\\rangle_y$ 分别为泡利 $X,Y$ 矩阵的正特征值对应的特征向量。 该训练集在密度矩阵的表示下为:\n",
"\n",
"$$\n",
"\\rho_0 = \\left[\\begin{array}{ccc}\n",
"1 & 0\\\\\n",
"0 & 0\n",
"\\end{array}\\right], \n",
"\\rho_1 = \\left[\\begin{array}{ccc}\n",
"0 & 0\\\\\n",
"0 & 1\n",
"\\end{array}\\right], \n",
"\\rho_2 = \\left[\\begin{array}{ccc}\n",
"0.5 & 0.5\\\\\n",
"0.5 & 0.5\n",
"\\end{array}\\right], \n",
"\\rho_3 = \\left[\\begin{array}{ccc}\n",
"0.5 & -0.5 i\\\\\n",
"0.5i & 0.5\n",
"\\end{array}\\right]. \n",
"\\tag{8}\n",
"$$\n",
"\n",
"任何一个单量子比特量子态都可以写为上述 4 个态的线性组合。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class LOCC_Train(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC_Train, self).__init__()\n",
" \n",
" # 初始化 LOCCNet\n",
" self.parties = list()\n",
" \n",
" # 添加第一个参与方 Alice\n",
" # 第一个参数 2 代表着 Alice 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(2, party_name=\"Alice\")\n",
" \n",
" # 添加第二个参与方 Bob\n",
" # 第一个参数 1 代表着 Bob 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(1, party_name=\"Bob\")\n",
" \n",
"\n",
" # 准备一个贝尔态\n",
" _state = fluid.dygraph.to_variable(bell_state(2))\n",
"# _state = fluid.dygraph.to_variable(isotropic_state(2, 0.8))\n",
"\n",
"\n",
" # 设置 Alice 本地操作的参数\n",
" self.theta_A = self.create_parameter(shape=[15], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, \n",
" seed=SEED), dtype=\"float64\")\n",
" # 设置 Bob 本地操作的参数\n",
" self.theta_B = self.create_parameter(shape=[4, 3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, \n",
" seed=SEED), dtype=\"float64\")\n",
"\n",
" # 训练集: 4 个线性独立态\n",
" _state0 = fluid.dygraph.to_variable(np.array([[1, 0], [0, 0]], dtype=np.complex128))\n",
" _state1 = fluid.dygraph.to_variable(np.array([[0, 0], [0, 1]], dtype=np.complex128))\n",
" _state2 = fluid.dygraph.to_variable(np.array([[0.5, 0.5], [0.5, 0.5]], dtype=np.complex128))\n",
" _state3 = fluid.dygraph.to_variable(np.array([[0.5, -0.5j], [0.5j, 0.5]], dtype=np.complex128))\n",
" self.init_states = [_state0, _state1, _state2, _state3]\n",
"\n",
" # 通过分配上述制备好的量子态初始化整个量子系统\n",
" self.set_init_status(_state, [(\"Alice\", 1), (\"Bob\", 0)])\n",
" self.set_init_status(_state0, [(\"Alice\", 0)])\n",
"\n",
" def LOCCNet(self):\n",
" \n",
" # 定义训练过程\n",
" loss = 0\n",
" temp_state = self.init_status\n",
" \n",
" # 开始训练\n",
" for init_state in self.init_states:\n",
" \n",
" # 重置 Alice 持有的量子比特 C 至训练集中的量子态\n",
" status = self.reset_state(temp_state, init_state, [(\"Alice\", 0)])\n",
"\n",
" # 定义 Alice 的本地操作\n",
" cirA = self.create_ansatz(\"Alice\")\n",
" cirA.universal_2_qubit_gate(self.theta_A, [0, 1])\n",
"\n",
" # 执行 Alice 的电路\n",
" status = cirA.run(status)\n",
" \n",
" # 测量得到四个可能的结果\n",
" status_A = self.measure(status, [(\"Alice\", 0), (\"Alice\", 1)], [\"00\", \"01\", \"10\", \"11\"])\n",
" \n",
" # Bob 根据测量结果选择不同的门作用在自己的量子比特上\n",
" for i, s in enumerate(status_A):\n",
" \n",
" # 定义 Bob 的本地操作\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" \n",
" # 作用单量子比特通用门\n",
" cirB.u3(*self.theta_B[i], 0)\n",
" \n",
" # 执行 Bob 的电路\n",
" status_B = cirB.run(s)\n",
" \n",
" # 仅留下 Bob 的量子比特 B\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" \n",
" # 将所有的测量结果的损失函数进行累加\n",
" loss += 1 - trace(matmul(init_state, status_fin.state)).real[0]\n",
" \n",
" return loss\n",
"\n",
" # 存储训练结束后的最优参数\n",
" def save_module(self):\n",
" theta = np.array([self.theta_A.numpy(), self.theta_B.numpy()])\n",
" np.save('parameters/QT_LOCCNet', theta)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ITR = 150 # 设置优化循环次数\n",
"LR = 0.2 # 设置学习速率\n",
"SEED = 999 # 固定本地操作中参数的初始化随机数种子\n",
"\n",
"# 初始化动态图模式\n",
"with fluid.dygraph.guard():\n",
"\n",
" net = LOCC_Train()\n",
" \n",
" # 选择 Adam 优化器\n",
" opt = fluid.optimizer.AdamOptimizer(learning_rate=LR, parameter_list= net.parameters())\n",
" \n",
" # 记录损失\n",
" loss_list = []\n",
" \n",
" # 开始优化循环\n",
" for itr in range(ITR):\n",
"\n",
" # 向前传播计算损失函数\n",
" loss = net.LOCCNet()\n",
" \n",
" # 反向传播优化损失函数\n",
" loss.backward()\n",
" opt.minimize(loss)\n",
" \n",
" # 清除梯度\n",
" net.clear_gradients()\n",
" \n",
" # 记录训练结果\n",
" loss_list.append(loss.numpy()[0])\n",
" if itr % 10 == 0:\n",
" print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
" \n",
" # 保存参数\n",
" net.save_module()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 基准测试\n",
"\n",
"如果想要更快捷的感受效果,我们还提供**加载预先训练好的电路参数**并直接测试性能。"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T14:23:22.264769Z",
"start_time": "2021-01-26T14:23:22.072281Z"
}
},
"outputs": [],
"source": [
"class LOCC_Test(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC_Test, self).__init__()\n",
" \n",
" self.parties = list()\n",
" self.add_new_party(2, party_name=\"Alice\")\n",
" self.add_new_party(1, party_name=\"Bob\")\n",
" \n",
" self.theta_A = fluid.dygraph.to_variable(para[0])\n",
" self.theta_B = fluid.dygraph.to_variable(para[1])\n",
" \n",
" _state = fluid.dygraph.to_variable(bell_state(2))\n",
" random_state = density_op_random(n=1)\n",
" self._state0 = fluid.dygraph.to_variable(random_state)\n",
" self.set_init_status(_state, [(\"Alice\", 1), (\"Bob\", 0)])\n",
" self.set_init_status(self._state0, [(\"Alice\", 0)])\n",
" \n",
"\n",
" def benchmark(self):\n",
" input_state = self.init_status\n",
" cirA = self.create_ansatz(\"Alice\")\n",
" cirA.universal_2_qubit_gate(self.theta_A, [0, 1])\n",
"\n",
" status = cirA.run(input_state)\n",
" status_A = self.measure(status, [(\"Alice\", 0), (\"Alice\", 1)], [\"00\", \"01\", \"10\", \"11\"])\n",
" fid_list = []\n",
"\n",
" for i, s in enumerate(status_A):\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.u3(*self.theta_B[i], 0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self._state0.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid*status_fin.prob.numpy()[0])\n",
" fid_ave = sum(fid_list)\n",
" \n",
" return fid_ave"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-26T14:24:43.301269Z",
"start_time": "2021-01-26T14:24:11.028218Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"平均保真度 = 1.0 , 标准差 = 5.695904177817817e-07\n"
]
}
],
"source": [
"with fluid.dygraph.guard():\n",
"\n",
" # 加载预先训练的电路参数\n",
" para = np.load('parameters/QT_LOCCNet_Trained.npy', allow_pickle=True)\n",
" np.random.seed(999) # 固定生成传输态的随机种子\n",
" num_state = 200 # 设置随机态的生成数量\n",
" list_fid = [] # 用于记录保真度\n",
" \n",
" # 采样\n",
" for idx in range(num_state):\n",
" list_fid.append(LOCC_Test().benchmark())\n",
"\n",
" print('平均保真度 =', np.around(sum(list_fid)/len(list_fid), 4), ', 标准差 =', np.std(list_fid))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 结论\n",
"\n",
"从以上数值实验的结果可以看出,基于 LOCCNet 我们成功地学习出了量子隐形传态协议。 最初的隐形传态协议旨在传输单量子比特量子态,无法直接推广到多量子比特的情形。 相比之下,LOCCNet 为寻找多量子比特情形下的隐形传态协议提供了可能, 更是可以训练得到更一般的通过量子纠缠和 LOCC 实现不同节点间量子信道的模拟。 此外,感兴趣的读者可以尝试各类量子噪声会如何影响 LOCCNet 训练的效果。 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## 参考文献\n",
"\n",
"[1] Bennett, Charles H., et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 70.13 (1993): 1895.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895)\n",
"\n",
"[2] Boschi, Danilo, et al. \"Experimental realization of teleporting an unknown pure quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 80.6 (1998): 1121.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.80.1121)\n",
"\n",
"[3] Bouwmeester, Dik, et al. \"Experimental quantum teleportation.\" [Nature 390.6660 (1997): 575-579.](https://www.nature.com/articles/37539)\n",
"\n",
"[4] Vidal, Guifre, and Christopher M. Dawson. \"Universal quantum circuit for two-qubit transformations with three controlled-NOT gates.\" [Physical Review A 69.1 (2004): 010301.](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.69.010301)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Learning the Quantum Teleportation Protocol\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"Quantum teleportation is another important task that can be completed by Local Operations and Classical Communication (LOCC) protocols, which transfers quantum information between two spatially separated communication nodes (only classical communication channel allowed) with the help of entanglement. In this tutorial, we will first briefly review the original teleportation protocol and simulate it with Paddle Quantum. Then, we will go through how to learn a teleportation protocol with LOCCNet.\n",
" \n",
"## The original quantum teleportation protocol\n",
"\n",
"\n",
"The original teleportation protocol was proposed by C. H. Bennett et al. in 1993 [1] and experimentally verified in 1997 with photonic platforms [2-3]. The workflow is illustrated in the figure below. Following the convention, this process requires 2 communication nodes or parties, namely $A$ (Alice) and $B$ (Bob). For simplicity, we only consider transferring a single-qubit quantum state $|\\psi\\rangle_C$ and this requires 3 qubits in total including the pre-shared maximally entangled state $|\\Phi^+\\rangle_{AB} = \\frac{1}{\\sqrt{2}}(|00\\rangle + |11\\rangle)$. Alice holds systems A and C,Bob holds system B. **Note: Only quantum information is transferred, not the physical qubits.** \n",
"\n",
"\n",
"<center><img src=\"figures/teleportation-fig-circuit.jpg\" height=\"400\" width=\"600\"></center>\n",
"<div style=\"text-align:center\">Figure 1: Quantum teleportation: Transferring quantum state $|\\psi\\rangle$ from Alice to receiver Bob. </div>\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step I:** At the very beginning, the system state can be described as\n",
"\n",
"$$\n",
"\\lvert\\varphi_{0}\\rangle \n",
"= \\lvert\\psi\\rangle_{C}\\otimes \\lvert\\Phi^+\\rangle_{AB} \n",
"= \\frac{1}{\\sqrt{2}}\\big[\\alpha\\lvert0\\rangle(\\lvert00\\rangle + \\lvert11\\rangle)+\\beta\\lvert1\\rangle(\\lvert00\\rangle + \\lvert11\\rangle)\\big],\n",
"\\tag{1}\n",
"$$\n",
"\n",
"where the quantum state Alice want to transmit is $|\\psi\\rangle_C = \\alpha|0\\rangle_C + \\beta|1\\rangle_C$ and the coefficients $\\alpha, \\beta \\in \\mathbb{C}$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step II:** Alice applies a CNOT gate, and the resulting state is\n",
"\n",
"$$\n",
"|\\varphi_1\\rangle \n",
"= \\frac{1}{\\sqrt{2}}\\big[\\alpha\\lvert0\\rangle(\\lvert00\\rangle + \\lvert11\\rangle)+\\beta\\lvert1\\rangle(\\lvert10\\rangle + \\lvert01\\rangle)\\big],\n",
"\\tag{2}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step III:** Alice applies a Hadamard gate, and the system state becomes $|\\varphi_2\\rangle$ \n",
"\n",
"$$\n",
"|\\varphi_2\\rangle = \\frac{1}{2}\\big[\\alpha(\\lvert0\\rangle + \\lvert1\\rangle)(\\lvert00\\rangle + \\lvert11\\rangle)+\\beta(\\lvert0\\rangle - \\lvert1\\rangle)(\\lvert10\\rangle + \\lvert01\\rangle)\\big],\n",
"\\tag{3}\n",
"$$\n",
"\n",
"The above state can be rearranged to\n",
"\n",
"$$\n",
"\\lvert\\varphi_{2}\\rangle = \\frac{1}{2}\\big[\\lvert00\\rangle(\\alpha\\lvert0\\rangle + \\beta\\lvert1\\rangle) + \\lvert01\\rangle(\\alpha\\lvert1\\rangle + \\beta\\lvert0\\rangle) + \\lvert10\\rangle(\\alpha\\lvert0\\rangle - \\beta\\lvert1\\rangle) + \\lvert11\\rangle(\\alpha\\lvert1\\rangle - \\beta\\lvert0\\rangle)\\big].\n",
"\\tag{4}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Step IV:** Alice measures both of her qubits in the computational basis $\\{|00\\rangle, |01\\rangle, |10\\rangle, |11\\rangle\\}$ and send the results $m_1m_2$ to Bob with a classical channel. There are 4 distinct possibilities: $m_1m_2 \\in \\{ 00, 01,10, 11\\}$. Then, Bob implements certain operations correspondingly on his qubit based on the received messages.\n",
"\n",
"\n",
"- If the measurement result is $m_1m_2 = 00$, Bob's state will be $\\alpha\\lvert0\\rangle + \\beta\\lvert1\\rangle$, which is the state Alice want to transmit $\\lvert\\psi\\rangle_C$. No operations are needed and the teleportation is finished.\n",
"- If the measurement result is $m_1m_2 = 01$, Bob's state will be $\\alpha\\lvert1\\rangle + \\beta\\lvert0\\rangle$. Bob needs to act the $X$ gate on his qubit.\n",
"- If the measurement result is $m_1m_2 = 10$, Bob's state will be $\\alpha\\lvert0\\rangle - \\beta\\lvert1\\rangle$. Bob needs to act the $Z$ gate on his qubit.\n",
"- If the measurement result is $m_1m_2 = 11$, Bob's state will be $\\alpha\\lvert1\\rangle - \\beta\\lvert0\\rangle$. Bob needs to act the $X$ gate followed by the $Z$ gate on his qubit.\n",
"\n",
"In the next section, we will go through how to simulate the teleportation protocol with Paddle Quantum."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulation with Paddle Quantum\n",
"\n",
"First, we need to import all the dependencies:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T08:58:58.723528Z",
"start_time": "2021-01-25T08:58:52.538986Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet\n",
"from paddle.complex import matmul, trace\n",
"from paddle_quantum.utils import state_fidelity\n",
"from paddle_quantum.state import bell_state, isotropic_state, density_op_random"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Initialize the quantum state, and define the quantum circuit and teleportation protocol."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T08:58:58.778103Z",
"start_time": "2021-01-25T08:58:58.740654Z"
}
},
"outputs": [],
"source": [
"class LOCC(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC, self).__init__()\n",
" \n",
" # Initialize LOCCNet\n",
" self.parties = list()\n",
" \n",
" # Add the first party Alice \n",
" # The first parameter 2 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(2, party_name=\"Alice\")\n",
" \n",
" # Add the second party Bob\n",
" # The first parameter 1 stands for how many qubits B holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(1, party_name=\"Bob\")\n",
" \n",
"\n",
" # Create a bell state\n",
" _state = fluid.dygraph.to_variable(bell_state(2))\n",
"# _state = fluid.dygraph.to_variable(isotropic_state(2, 0.8))\n",
"\n",
" \n",
" # Generate random pure quantum states for teleportation\n",
" random_state = density_op_random(n=1, real_or_complex=2, rank=1)\n",
" self.state_C = fluid.dygraph.to_variable(random_state)\n",
" \n",
" # Initialize the system by distributing states between Alice and Bob\n",
" # (\"Alice\", 0) refers to qubit C\n",
" # (\"Alice\", 1) refers to qubit A\n",
" # (\"Bob\", 0) refers to qubit B\n",
"# print('Pre-shared entanglement state is:\\n', _state.numpy())\n",
" self.set_init_status(self.state_C, [(\"Alice\", 0)])\n",
" self.set_init_status(_state, [(\"Alice\", 1), (\"Bob\", 0)])\n",
"\n",
"\n",
" def teleportation(self):\n",
" status = self.init_status\n",
" \n",
" # Create Alice's local operations \n",
" cirA = self.create_ansatz(\"Alice\")\n",
" cirA.cnot([0, 1])\n",
" cirA.h(0)\n",
" \n",
" # Run circuit\n",
" status = cirA.run(status)\n",
" \n",
" # Alice measures both of her qubits C and A\n",
" status_A = self.measure(status, [(\"Alice\", 0), (\"Alice\", 1)], [\"00\", \"01\", \"10\", \"11\"])\n",
" \n",
" # Record average fidelity \n",
" fid_list = []\n",
"\n",
" # Bob applies different gates on his qubits depending on the measurement result of Alice\n",
" for i, s in enumerate(status_A):\n",
" \n",
" # Bob's circuit\n",
" if status_A[i].measured_result == '00':\n",
" \n",
" # Create Bob's local operations \n",
" cirB = self.create_ansatz(\"Bob\")\n",
" \n",
" # Run circuit\n",
" status_B = cirB.run(s) \n",
" \n",
" # Trace out the measured qubits C and A\n",
" # Leaving out only Bob’s qubit B\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
"\n",
" # Calculate the fidelity between the teleported state and the original state\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" \n",
" elif status_A[i].measured_result == '01':\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.x(0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" elif status_A[i].measured_result == '10':\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.z(0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" elif status_A[i].measured_result == '11':\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.x(0)\n",
" cirB.z(0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self.state_C.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid * status_fin.prob.numpy()[0])\n",
" fid_avg = sum(fid_list)\n",
" \n",
" return fid_avg"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we randomly generate 200 pure quantum states and use state fidelity $F$ to benchmark the teleportation protocol, where\n",
"\n",
"$$\n",
"F(\\rho,\\sigma) \\equiv \\text{tr}\\big( \\sqrt{\\sqrt{\\rho}\\sigma \\sqrt{\\rho}} \\big)^2.\n",
"\\tag{5}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T08:59:16.906737Z",
"start_time": "2021-01-25T08:58:58.817863Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Teleportation_Fidelity_Avg: 1.0 , std= 1.0610206920288784e-14\n"
]
}
],
"source": [
"with fluid.dygraph.guard():\n",
" \n",
" np.random.seed(999) # Fix random seed\n",
" num_state = 200 # Number of random states generated\n",
" list_fid = [] # Record the fidelity\n",
" \n",
" # Start sampling\n",
" for idx in range(num_state):\n",
" list_fid.append(LOCC().teleportation())\n",
"\n",
" print('Teleportation_Fidelity_Avg:', np.around(sum(list_fid)/len(list_fid), 4), ', std=', np.std(list_fid))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Note:** We want to point out the validity of this protocol relies on the quality of the pre-shared entanglement. Readers can change the entangled state from `bell_state(2)` to `isotropic_state(2, p)` and see how quantum noises will influence the teleportation performance. Recall the definition of isotropic states,\n",
"\n",
"\n",
"$$\n",
"\\rho_{\\text{iso}}(p) = p\\lvert\\Phi^+\\rangle \\langle\\Phi^+\\rvert + (1-p)\\frac{I}{4}, \\quad p \\in [0,1]\n",
"\\tag{6}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Learning a teleportation protocol with LOCCNet\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Training the protocol \n",
"\n",
"A general LOCC protocol can be classified by the number of classical communication rounds $r$. The original teleportation protocol is a one-round ($r=1$) protocol. For simplicity, we also restrict the communication rounds to 1. Compare to the original protocol, we use parametrized quantum circuits (PQC) to replace the fixed gates $U\\in\\{X,Z\\}$ Bob applied on his qubit with a general rotation gate $U_3$ on the Bloch sphere. \n",
"\n",
"$$\n",
"U_3(\\theta, \\phi, \\varphi) =\n",
"\\begin{bmatrix}\n",
"\\cos(\\frac{\\theta}{2}) & -e^{i\\varphi}\\sin(\\frac{\\theta}{2})\\\\\n",
"e^{i\\phi}\\sin(\\frac{\\theta}{2}) & e^{i(\\phi+\\varphi)} \\cos(\\frac{\\theta}{2})\n",
"\\end{bmatrix}.\n",
"\\tag{7}\n",
"$$\n",
"\n",
"This would bring us a more powerful searching capability in finding practical LOCC protocols. Similarly, we change Alice's local operations to a more general PQC called the `universal_2_qubit_gate(theta, which_qubit=[0,1])` [4]. We summarize the workflow below: \n",
"\n",
"1. Alice applies a 2-qubit PQC on her qubits.\n",
"2. Alice measures both of her qubits in the computational basis and communicates with Bob through a classical channel.\n",
"3. There are 4 possible measurement results: $m_1m_2 \\in \\{00, 01, 10, 11\\}$. Bob needs to act different operations corresponding to these measurement results. After Bob's local operations, the state on his qubit collapses to $\\lvert \\psi\\rangle_{B}$. \n",
"4. Calculate the overlap $O$ between $\\lvert \\psi\\rangle_{B}$ and $\\lvert\\psi_C\\rangle$ (pure states). LOCCNet framework only supports density matrix formulation and hence we have to rewrite them as $\\rho_{B} = |\\psi\\rangle\\langle\\psi|_B$ and $\\rho_{C} = |\\psi\\rangle\\langle\\psi|_C$. Then, $O = \\text{Tr}(\\rho_C\\rho_{B})$. For pure states, this metric is simply the state fidelity.\n",
"5. Set the accumulated loss function over 4 possible measurement results as $L = \\sum_{m_1m_2} \\big(1-\\text{Tr}(\\rho_C\\rho_{B})\\big)$, and use gradient-based optimization methods to update circuit parameters and hence minimize the loss function.\n",
"6. Repeat steps 1-5 until the loss function converges to the global minimum.\n",
"7. Generate an ensemble of arbitrary states $\\{\\lvert\\psi_C\\rangle\\}$, and benchmark the trained protocol with average state fidelity.\n",
"\n",
"\n",
"<center><img src=\"figures/teleportation-fig-LOCCNet.png\" height=\"400\" width=\"600\"></center>\n",
"<div style=\"text-align:center\">Figure 2: Learning a teleportation protocol with LOCCNet. </div>\n",
"\n",
"\n",
"**Note:** In order to make sure the parameters in parameterized quantum circuit is valid for all state after training, we set the training set as 4 linear independent states, which is $\\{|0\\rangle\\langle 0|,|1\\rangle\\langle 1|,|+\\rangle\\langle +|,|+\\rangle\\langle +|_y\\}$ or in the density matrix form:\n",
"\n",
"$$\n",
"\\rho_0 = \\left[\\begin{array}{ccc}\n",
"1 & 0\\\\\n",
"0 & 0\n",
"\\end{array}\\right], \n",
"\\rho_1 = \\left[\\begin{array}{ccc}\n",
"0 & 0\\\\\n",
"0 & 1\n",
"\\end{array}\\right], \n",
"\\rho_2 = \\left[\\begin{array}{ccc}\n",
"0.5 & 0.5\\\\\n",
"0.5 & 0.5\n",
"\\end{array}\\right], \n",
"\\rho_3 = \\left[\\begin{array}{ccc}\n",
"0.5 & -0.5 i\\\\\n",
"0.5i & 0.5\n",
"\\end{array}\\right]. \n",
"\\tag{8}\n",
"$$\n",
"\n",
"Any single qubit state can be written as a combination of the above 4 linear independent states in $\\mathcal{H}^{2\\times 2}$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T08:55:21.137306Z",
"start_time": "2021-01-25T08:55:21.083649Z"
}
},
"outputs": [],
"source": [
"class LOCC_Train(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC_Train, self).__init__()\n",
" \n",
" # Initialize LOCCNet\n",
" self.parties = list()\n",
" \n",
" # Add the first party Alice \n",
" # The first parameter 2 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(2, party_name=\"Alice\")\n",
" \n",
" # Add the second party Bob\n",
" # The first parameter 1 stands for how many qubits B holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(1, party_name=\"Bob\")\n",
" \n",
"\n",
" # Create a bell state\n",
" _state = fluid.dygraph.to_variable(bell_state(2))\n",
"# _state = fluid.dygraph.to_variable(isotropic_state(2, 0.8))\n",
"\n",
"\n",
" # Initialize Alice‘s parameters\n",
" self.theta_A = self.create_parameter(shape=[15], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, \n",
" seed=SEED), dtype=\"float64\")\n",
" # Initialize Bob's parameters\n",
" self.theta_B = self.create_parameter(shape=[4, 3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, \n",
" seed=SEED), dtype=\"float64\")\n",
"\n",
" # Training set: 4 linear independent states\n",
" _state0 = fluid.dygraph.to_variable(np.array([[1, 0], [0, 0]], dtype=np.complex128))\n",
" _state1 = fluid.dygraph.to_variable(np.array([[0, 0], [0, 1]], dtype=np.complex128))\n",
" _state2 = fluid.dygraph.to_variable(np.array([[0.5, 0.5], [0.5, 0.5]], dtype=np.complex128))\n",
" _state3 = fluid.dygraph.to_variable(np.array([[0.5, -0.5j], [0.5j, 0.5]], dtype=np.complex128))\n",
" self.init_states = [_state0, _state1, _state2, _state3]\n",
"\n",
" # Initialize the system by distributing states between Alice and Bob\n",
" self.set_init_status(_state, [(\"Alice\", 1), (\"Bob\", 0)])\n",
" self.set_init_status(_state0, [(\"Alice\", 0)])\n",
"\n",
" def LOCCNet(self):\n",
" \n",
" # Define the training process\n",
" loss = 0\n",
" temp_state = self.init_status\n",
" \n",
" # Training\n",
" for init_state in self.init_states:\n",
" \n",
" # Reset Alice's first qubit C to states in training set\n",
" status = self.reset_state(temp_state, init_state, [(\"Alice\", 0)])\n",
"\n",
" # Define Alice's local operations\n",
" cirA = self.create_ansatz(\"Alice\")\n",
" cirA.universal_2_qubit_gate(self.theta_A, [0, 1])\n",
"\n",
" # Run circuit\n",
" status = cirA.run(status)\n",
" \n",
" # Obtain 4 possible measurement results\n",
" status_A = self.measure(status, [(\"Alice\", 0), (\"Alice\", 1)], [\"00\", \"01\", \"10\", \"11\"])\n",
" \n",
" # Bob needs to apply different operation on his qubits, depending on the measurement results\n",
" for i, s in enumerate(status_A):\n",
" \n",
" # Define Bob's local operations\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" \n",
" # Apply a universal single qubit gate\n",
" cirB.u3(*self.theta_B[i], 0)\n",
" \n",
" # Run circuit\n",
" status_B = cirB.run(s)\n",
" \n",
" # Trace out the measured qubits C and A\n",
" # Leaving out only Bob’s qubit B\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" \n",
" # Summing up the loss function for all possible measurement results\n",
" loss += 1 - trace(matmul(init_state, status_fin.state)).real[0]\n",
" \n",
" return loss\n",
"\n",
" # Save the optimized parameters\n",
" def save_module(self):\n",
" theta = np.array([self.theta_A.numpy(), self.theta_B.numpy()])\n",
" np.save('parameters/QT_LOCCNet', theta)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T08:57:14.202778Z",
"start_time": "2021-01-25T08:55:21.148860Z"
}
},
"outputs": [],
"source": [
"ITR = 150 # Number of iterations\n",
"LR = 0.2 # Set up learning rate\n",
"SEED = 999 # Fix random seed for parameters in PQC\n",
"\n",
"# Initialize the dynamic graph mode\n",
"with fluid.dygraph.guard():\n",
"\n",
" net = LOCC_Train()\n",
" \n",
" # Choose the Adam optimizer\n",
" opt = fluid.optimizer.AdamOptimizer(learning_rate=LR, parameter_list= net.parameters())\n",
" \n",
" # Record loss\n",
" loss_list = []\n",
" \n",
" # Optimization loop\n",
" for itr in range(ITR):\n",
" \n",
" # Forward propagation to calculate loss function\n",
" loss = net.LOCCNet()\n",
" \n",
" # Backpropagation\n",
" loss.backward()\n",
" opt.minimize(loss)\n",
" \n",
" # Clean gradients\n",
" net.clear_gradients()\n",
" \n",
" # Record the learning process\n",
" loss_list.append(loss.numpy()[0])\n",
" if itr % 10 == 0:\n",
" print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
" \n",
" # Save parameters\n",
" net.save_module()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Benchmark\n",
"\n",
"If you don't want to spend time on training, you can **load the pre-trained circuit parameters** and directly test the performance."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T08:59:22.381620Z",
"start_time": "2021-01-25T08:59:22.368180Z"
}
},
"outputs": [],
"source": [
"class LOCC_Test(LoccNet):\n",
" def __init__(self):\n",
" super(LOCC_Test, self).__init__()\n",
" \n",
" self.parties = list()\n",
" self.add_new_party(2, party_name=\"Alice\")\n",
" self.add_new_party(1, party_name=\"Bob\")\n",
" \n",
" self.theta_A = fluid.dygraph.to_variable(para[0])\n",
" self.theta_B = fluid.dygraph.to_variable(para[1])\n",
" \n",
" _state = fluid.dygraph.to_variable(bell_state(2))\n",
" random_state = density_op_random(n=1)\n",
" self._state0 = fluid.dygraph.to_variable(random_state)\n",
" self.set_init_status(_state, [(\"Alice\", 1), (\"Bob\", 0)])\n",
" self.set_init_status(self._state0, [(\"Alice\", 0)])\n",
" \n",
"\n",
" def benchmark(self):\n",
" input_state = self.init_status\n",
" cirA = self.create_ansatz(\"Alice\")\n",
" cirA.universal_2_qubit_gate(self.theta_A, [0, 1])\n",
"\n",
" status = cirA.run(input_state)\n",
" status_A = self.measure(status, [(\"Alice\", 0), (\"Alice\", 1)], [\"00\", \"01\", \"10\", \"11\"])\n",
" fid_list = []\n",
"\n",
" for i, s in enumerate(status_A):\n",
" cirB = self.create_ansatz(\"Bob\")\n",
" cirB.u3(*self.theta_B[i], 0)\n",
" status_B = cirB.run(s)\n",
" status_fin = self.partial_state(status_B, [(\"Bob\", 0)])\n",
" fid = state_fidelity(self._state0.numpy(), status_fin.state.numpy())**2\n",
" fid_list.append(fid*status_fin.prob.numpy()[0])\n",
" fid_ave = sum(fid_list)\n",
" \n",
" return fid_ave"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-25T09:00:01.169427Z",
"start_time": "2021-01-25T08:59:22.909640Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"LOCCNet_Fidelity_avg: 1.0 , std= 5.695904177817817e-07\n"
]
}
],
"source": [
"with fluid.dygraph.guard():\n",
"\n",
" # Load pre-trained circuit parameters\n",
" para = np.load('parameters/QT_LOCCNet_Trained.npy', allow_pickle=True)\n",
" np.random.seed(999) # Fix random seed\n",
" num_state = 200 # Number of random states generated\n",
" list_fid = [] # Record the fidelity\n",
" \n",
" # Start sampling\n",
" for idx in range(num_state):\n",
" list_fid.append(LOCC_Test().benchmark())\n",
"\n",
" print('LOCCNet_Fidelity_avg:', np.around(sum(list_fid)/len(list_fid), 4), ', std=', np.std(list_fid))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"Based on LOCCNet we successfully learned a teleportation protocol with a noiseless pre-shared Bell state. The original teleportation protocol was designed to transfer a single-qubit quantum state. It is not clear how it can be generalize to the multi-qubit case. By comparison, LOCCNet provides the possibility of training a teleportation protocol for multi-qubit quantum states. Also, it will be an interesting question to ask how robust LOCCNet will be against various noises. On the other hand, the teleportation protocol could be viewed as a special case of simulation the identity channel $\\mathcal{E}_I$ where Alice sends $\\psi$ and Bob receives $\\mathcal{E}_I(\\psi)$. This idea can be extended to simulate many other channels including the depolarizing channel $\\mathcal{E}_{D}$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## References\n",
"\n",
"[1] Bennett, Charles H., et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 70.13 (1993): 1895.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895)\n",
"\n",
"[2] Boschi, Danilo, et al. \"Experimental realization of teleporting an unknown pure quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" [Physical Review Letters 80.6 (1998): 1121.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.80.1121)\n",
"\n",
"[3] Bouwmeester, Dik, et al. \"Experimental quantum teleportation.\" [Nature 390.6660 (1997): 575-579.](https://www.nature.com/articles/37539)\n",
"\n",
"[4] Vidal, Guifre, and Christopher M. Dawson. \"Universal quantum circuit for two-qubit transformations with three controlled-NOT gates.\" [Physical Review A 69.1 (2004): 010301.](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.69.010301)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"id": "worse-village",
"metadata": {},
"source": [
"# 利用 LOCC 来进行两方量子态分辨\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"id": "important-allowance",
"metadata": {},
"source": [
"## 概述\n",
"\n",
"量子态分辨(Quantum State Discrimination,QSD)[1-2] 是量子通信,量子计算和量子密码学中的一个基本问题。本教程展示了如何通过本地量子操作和经典通信(LOCC)来区分满足 $\\langle\\psi\\lvert\\phi\\rangle=0$ 的两个正交的两方纯态 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$。本教程中使用的方法和理论参考了 [3] 。\n"
]
},
{
"cell_type": "markdown",
"id": "accomplished-horror",
"metadata": {},
"source": [
"## 寻找一个态分辨协议\n",
"\n",
"首先,我们将需要处理的问题定义清楚。考虑两个在空间上相隔一定距离的参与方 $ A $(Alice)和 $ B $(Bob)共享一对两个量子比特系统,该系统的状态 $|\\varphi\\rangle$ 由第三方 $C$(Charlie)提前制备好分发给参与方。Alice 和 Bob 只是被提前通知了 $|\\varphi\\rangle$ 可能是 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 两者其中之一,并且满足条件 $\\langle\\psi\\lvert\\phi\\rangle=0$。然后,Charlie 给参与方提供了很多 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 的拷贝并希望 Alice 和 Bob 可以通过这些来正确的分辨出他们现在持有的量子态究竟是 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 中的哪一个。\n",
"\n",
"在我们提供的 LOCCNet 框架下解决这样一个任务是很轻松的。方便起见,我们还是先采用单轮通讯协议 $r=1$ 的设计。 也就是说 Alice 和 Bob 之间只会进行一次经典通讯。具体的量子神经网络(QNN)见图 1。该任务中的关键步骤是确定损失函数 $ L $。这里我们设置 Alice 和 Bob 都需要对手中量子比特做一次测量,因此有 4 种可能的测量结果 $m_Am_B\\in\\{00, 01, 10, 11\\}$。为了能清晰地区分 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$, 我们不妨定义当测量结果为 $m_Am_B\\in\\{00, 10\\}$ 时将其分类为 $\\lvert\\psi\\rangle$。 类似地,如果参与方的测量结果为 $m_Am_B\\in\\{01, 11\\}$ 时将其分类为 $\\lvert\\phi\\rangle$。此步骤可以理解为在监督学习中为数据添加标签。通过以上标签,我们可以通过分辨协议出错的概率来定义损失函数,\n",
"\n",
"$$\n",
"L = p_{\\lvert\\psi\\rangle\\_01}+p_{\\lvert\\psi\\rangle\\_11}+p_{\\lvert\\phi\\rangle\\_10}+p_{\\lvert\\phi\\rangle\\_00}. \n",
"\\tag{1}\n",
"$$\n",
"\n",
"其中 $p_{\\lvert\\psi\\rangle\\_01}$ 代表输入态为 $\\lvert\\psi\\rangle$ 时测量 01 的概率(**注释:** 这是一种分类错误的情况)。接着我们可以通过优化算法来最小化损失函数。\n",
"\n",
"<center><img src=\"figures/discrimination-fig-circuit.png\" height=\"300\" width=\"450\"></center>\n",
"<div style=\"text-align:center\">图 1. 用 LOCCNet 进行两方态分辨的协议示意图 </div>\n",
"\n",
"我们将整个流程进行如下总结:\n",
"\n",
"1. Alice 和 Bob 各自拥有一个二量子比特系统的一部分,整个系统的量子态可能为 $\\lvert\\psi\\rangle$ 或者 $\\lvert\\phi\\rangle$。这两个态正交且均是纯态。\n",
"2. Alice 对自己的量子比特进行酉变换 $U_A$(单比特通用门)。\n",
"3. Alice 在计算基(computational basis)上测量其量子比特,测量结果 $m_A\\in \\{0, 1\\}$。然后,她通过经典信道告知 Bob 自己的测量结果。\n",
"4. Bob 根据 Alice 的测量结果在自己量子比特上施加不同的门。如果 $ m_A = 0 $, Bob 对其持有的量子比特作用 $ U_{B0} $;如果 $ m_A = 1 $,则 Bob 施加 $ U_{B1} $。然后 Bob 测量自己的量子比特得到结果 $m_B \\in \\{0,1\\}$。**注意**:这里 $ U_{B0} $ 和 $ U_{B1} $ 都是单比特上的广义旋转门。\n",
"5. 计算损失函数 $L = p_{\\lvert\\psi\\rangle\\_01}+p_{\\lvert\\psi\\rangle\\_11}+ p_{\\lvert\\phi\\rangle\\_10}+ p_{\\lvert\\phi\\rangle\\_00}$,并使用基于梯度的优化方法。\n",
"6. 重复 1-5,直到损失函数收敛。\n",
"7. Charlie 在 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 两个之间随机选一个,并检验 Alice 和 Bob 的分辨方案是否可以正确地进行分辨。\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "stuffed-soccer",
"metadata": {},
"source": [
"## Paddle Quantum 代码实现\n",
"\n",
"首先,我们导入相关的依赖包。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "crazy-mention",
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:46:01.773791Z",
"start_time": "2021-01-27T09:45:56.319880Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.stats import unitary_group\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet"
]
},
{
"cell_type": "markdown",
"id": "abandoned-divide",
"metadata": {},
"source": [
"Charlie 需要随机生成两个正交态 $\\lvert\\psi\\rangle$ 以及 $\\lvert\\phi\\rangle$。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "seasonal-enemy",
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:46:01.811464Z",
"start_time": "2021-01-27T09:46:01.801909Z"
}
},
"outputs": [],
"source": [
"def states_orthogonal_random(n, num=2):\n",
" \n",
" # 随机生成两个正交态\n",
" assert num <= 2 ** n, \"return too many orthognal states\"\n",
" U = unitary_group.rvs(2 ** n)\n",
" return_list = []\n",
" for i in range(num):\n",
" return_list.append(U[i])\n",
" return return_list"
]
},
{
"cell_type": "markdown",
"id": "necessary-think",
"metadata": {},
"source": [
"下面是我们代码的主要部分,它定义了 Alice 和 Bob 的本地量子操作和损失函数。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "attempted-therapy",
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:46:02.017296Z",
"start_time": "2021-01-27T09:46:01.990328Z"
}
},
"outputs": [],
"source": [
"class Net (LoccNet):\n",
" def __init__(self):\n",
" super(Net, self).__init__()\n",
" \n",
" # 添加第一个参与方 Alice\n",
" # 第一个参数 1 代表着 Alice 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(1, party_name='Alice')\n",
" \n",
" # 添加第二个参与方 Bob\n",
" # 第一个参数 1 代表着 Bob 手里有几个量子比特\n",
" # 第二个参数代表着参与方的名字\n",
" self.add_new_party(1, party_name='Bob')\n",
" \n",
" # 初始化 Alice 的参数\n",
" self.theta1 = self.create_parameter(shape=[3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, seed=SEED),\n",
" dtype=\"float64\")\n",
" # 初始化 Bob 的参数\n",
" # Bob 要准备两个电路来应对两个不同的测量结果\n",
" self.theta2 = self.create_parameter(shape=[3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, seed=SEED),\n",
" dtype=\"float64\")\n",
" self.theta3 = self.create_parameter(shape=[3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, seed=SEED),\n",
" dtype=\"float64\")\n",
" \n",
" # 将输入态写成密度矩阵形式\n",
" _states = states_orthogonal_random(2)\n",
" _states = [fluid.dygraph.to_variable(np.outer(init_state, init_state.conjugate())) for init_state in _states]\n",
" \n",
" # 初始化整个量子系统并分配量子态\n",
" self.set_init_status(_states[0], [('Alice', 0), ('Bob', 0)])\n",
" self.psi = self.init_status\n",
" self.phi = self.reset_state(self.init_status, _states[1], [('Alice', 0), ('Bob', 0)])\n",
"\n",
" def A_circuit(self, theta, state, res):\n",
" # Alice 的电路\n",
" cir = self.create_ansatz('Alice')\n",
" # 添加单量子比特通用门\n",
" cir.u3(*theta, 0)\n",
" # 运行电路\n",
" after_state = cir.run(state)\n",
" # 测量电路,记录结果 \n",
" after_state = self.measure(status=after_state, which_qubits=('Alice', 0), results_desired=res)\n",
" return after_state\n",
"\n",
" def B_circuit(self, theta, state, res):\n",
" # Bob 的电路\n",
" cir = self.create_ansatz('Bob')\n",
" # 添加单量子比特通用门\n",
" cir.u3(*theta, 0)\n",
" # 运行电路\n",
" after_state = cir.run(state)\n",
" # 测量电路,记录结果 \n",
" after_state = self.measure(status=after_state, which_qubits=('Bob', 0), results_desired=res)\n",
" return after_state\n",
" \n",
" def forward(self):\n",
" # 训练过程\n",
" # Alice 操作过后的量子态\n",
" phi = self.A_circuit(theta=self.theta1, state=self.phi, res=['0', '1'])\n",
" psi = self.A_circuit(theta=self.theta1, state=self.psi, res=['0', '1'])\n",
"\n",
" # 定义损失函数\n",
" loss = 0\n",
" for each_phi in phi:\n",
" if each_phi.measured_result == '0':\n",
" # 我们要称之为 phi_1 ,代表着把 phi 预测为 psi\n",
" phi_1 = self.B_circuit(self.theta2, state=each_phi, res='1')\n",
" loss += phi_1.prob\n",
" elif each_phi.measured_result == '1':\n",
" psi_1 = self.B_circuit(self.theta3, state=each_phi, res='1')\n",
" loss += psi_1.prob\n",
"\n",
" for each_psi in psi:\n",
" if each_psi.measured_result == '0':\n",
" phi_0 = self.B_circuit(self.theta2, state=each_psi, res='0')\n",
" loss += phi_0.prob\n",
" elif each_psi.measured_result == '1':\n",
" psi_0 = self.B_circuit(self.theta3, state=each_psi, res='0')\n",
" loss += psi_0.prob\n",
"\n",
" return loss\n",
"\n",
" def evaluate(self):\n",
" # 测试过程\n",
" choice = np.random.choice(['phi', 'psi'])\n",
" if choice == 'phi':\n",
" self.status = self.phi\n",
" else:\n",
" self.status = self.psi\n",
" print('Charlie 选择的态是', choice)\n",
"\n",
" # Alice 的操作\n",
" status = self.A_circuit(theta=self.theta1, state=self.status, res=['0', '1'])\n",
" # Bob 的操作 \n",
" result_0 = list()\n",
" result_1 = list()\n",
" for each_status in status:\n",
" if each_status.measured_result == '0':\n",
" phi = self.B_circuit(theta=self.theta2, state=each_status, res=['0', '1'])\n",
" result_0.append(phi[0].prob.numpy())\n",
" result_0.append(phi[1].prob.numpy())\n",
"\n",
" elif each_status.measured_result == '1':\n",
" psi = self.B_circuit(theta=self.theta3, state=each_status, res=['0', '1'])\n",
" result_1.append(psi[0].prob.numpy())\n",
" result_1.append(psi[1].prob.numpy())\n",
" print(\"Alice 和 Bob 将这个态分辨为 phi 的概率为:\", result_0[0][0] + result_1[0][0])\n",
" print(\"Alice 和 Bob 将这个态分辨为 psi 的概率为:\", result_0[1][0] + result_1[1][0])\n",
" "
]
},
{
"cell_type": "markdown",
"id": "dense-kidney",
"metadata": {},
"source": [
"训练 Alice 和 Bob 的电路,然后随机选择两个正交态 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 之一以通过我们训练的电路,以检查它们是否可以区分。"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "theoretical-sampling",
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:46:12.711030Z",
"start_time": "2021-01-27T09:46:02.987658Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"itr 0: 0.41451205406019975\n",
"itr 10: 0.028321852355670925\n",
"itr 20: 0.03680755589712137\n",
"itr 30: 0.020398244027029454\n",
"itr 40: 0.008193744818255858\n",
"itr 50: 0.004566371117377564\n",
"itr 60: 0.0024181967231273298\n",
"itr 70: 0.0010470019789715651\n",
"itr 80: 0.00040675868734474884\n",
"itr 90: 0.00012631312259401648\n",
"最小损失: 3.333578751225666e-05\n",
"======================== 测试阶段 ===============================\n",
"Charlie 选择的态是 psi\n",
"Alice 和 Bob 将这个态分辨为 phi 的概率为: 2.1562924160659577e-05\n",
"Alice 和 Bob 将这个态分辨为 psi 的概率为: 0.9999784370758389\n",
"Charlie 选择的态是 phi\n",
"Alice 和 Bob 将这个态分辨为 phi 的概率为: 0.9999920892819899\n",
"Alice 和 Bob 将这个态分辨为 psi 的概率为: 7.910718009026491e-06\n"
]
}
],
"source": [
"ITR = 100 # 设置训练步数\n",
"LR = 0.1 # 设置学习速率\n",
"SEED = 999 # 固定 PQC 中参数的随机种子\n",
"np.random.seed(999)\n",
"\n",
"\n",
"# 初始化动态图模式\n",
"with fluid.dygraph.guard():\n",
" net = Net()\n",
" opt = fluid.optimizer.AdamOptimizer(learning_rate=LR, parameter_list=net.parameters())\n",
" loss_list = list()\n",
" # 通过梯度下降训练 LOCC 网络以进行 ITR 次迭代\n",
" for itr in range(ITR):\n",
" loss = net()\n",
" loss.backward()\n",
" opt.minimize(loss)\n",
" net.clear_gradients()\n",
" loss_list.append(loss.numpy()[0])\n",
" if itr % 10 == 0:\n",
" print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
" print(\"最小损失:\", loss_list[-1])\n",
" \n",
" print(\"======================== 测试阶段 ===============================\")\n",
" np.random.seed(10)\n",
" net.evaluate()\n",
" np.random.seed(6)\n",
" net.evaluate()"
]
},
{
"cell_type": "markdown",
"id": "ordered-concern",
"metadata": {},
"source": [
"## 结论\n",
"\n",
"从模拟结果可以看出,经过训练的量子电路可以准确地分辨出两个正交的量子态,精确度 $>99.9\\%$。这里有一个值得思考的问题:我们是否可以通过添加更多态来推广这种分辨方案?"
]
},
{
"cell_type": "markdown",
"id": "nasty-money",
"metadata": {},
"source": [
"---\n",
"## 参考文献\n",
"\n",
"[1] Barnett, Stephen M., and Sarah Croke. \"Quantum state discrimination.\" [Advances in Optics and Photonics 1.2 (2009): 238-278.](https://www.osapublishing.org/abstract.cfm?id=176580)\n",
"\n",
"[2] Chefles, Anthony. \"Quantum state discrimination.\" [Contemporary Physics 41.6 (2000): 401-424.](https://arxiv.org/abs/quant-ph/0010114)\n",
"\n",
"[3] Walgate, Jonathan, et al. \"Local distinguishability of multipartite orthogonal quantum states.\" [Physical Review Letters 85.23 (2000): 4972.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.85.4972)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.12"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Quantum State Discrimination\n",
"\n",
"\n",
"<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"Quantum state discrimination (QSD) [1-2] is a fundamental question in quantum communication, quantum computation, and quantum cryptography. In this tutorial, we will explain how to discriminate two orthogonal bipartite pure states $\\lvert\\psi\\rangle$ and $\\lvert\\phi\\rangle$, which satisfies $\\langle\\psi\\lvert\\phi\\rangle=0$, under the constraint of Local Operations and Classical Communication (LOCC). We refer all the theoretical details to the original paper [3].\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## QSD Protocol\n",
"\n",
"\n",
"Firstly, we want to make the problem definition clear. Consider two spatially separated parties $A$ (Alice) and $B$ (Bob) share a given two-qubit system, the system state is $\\lvert\\varphi\\rangle$ previously distributed by another party $C$ (Charlie). Alice and Bob were only notified that $\\lvert\\varphi\\rangle$ is either $\\lvert\\psi\\rangle$ or $\\lvert\\phi\\rangle$ (both are pure states), satisfying $\\langle\\psi\\lvert\\phi\\rangle=0$. Then, Charlie provides many copies of $\\lvert\\psi\\rangle$ and $\\lvert\\phi\\rangle$ to them, and he asks Alice and Bob to cooperate with each other to figure out which state they are actually sharing.\n",
"\n",
"\n",
"Solving this problem under our LOCCNet framework is trivial. As always, let's start with the simplest one-round LOCC protocol with a QNN architecture shown in Figure 1. Then, the difficult lies in the design of an appropriate loss function $L$. Since we choose to let both parties to measure their subsystem, there will be four possible measurement results $m_Am_B\\in\\{00, 01, 10, 11\\}$. To distinguish $\\lvert\\psi\\rangle$ and $\\lvert\\phi\\rangle$, we will label the former state with measurement results $m_Am_B\\in\\{00, 10\\}$ and the latter with $m_Am_B\\in\\{01, 11\\}$. This step can be understood as adding labels to the data in supervised learning. With these labels, we can define the loss function as the probability of guessing wrong label,\n",
"\n",
"$$\n",
"L = p_{\\lvert\\psi\\rangle\\_01}+p_{\\lvert\\psi\\rangle\\_11}+p_{\\lvert\\phi\\rangle\\_10}+p_{\\lvert\\phi\\rangle\\_00}.\n",
"\\tag{1}\n",
"$$\n",
"\n",
"where $p_{\\lvert\\psi\\rangle\\_01}$ stands for the probability of measuring 01 when the input state is $\\lvert\\psi\\rangle$. Then we can begin the training stage to minimize the loss function.\n",
"\n",
"\n",
"<center><img src=\"figures/discrimination-fig-circuit.png\" height=\"300\" width=\"450\"></center>\n",
"<div style=\"text-align:center\">Figure 1: Schematic diagram of state discrimination with LOCCNet. </div>\n",
"\n",
"\n",
"We summarize the workflow below:\n",
"\n",
"\n",
"1. Alice and Bob share a two-qubit system, which state is either $\\lvert\\psi\\rangle$ or $\\lvert\\phi\\rangle$. \n",
"2. Alice operates a general rotation gate $U_A$ on her qubit.\n",
"3. Alice measures her qubit on the computational basis, and the result $m_A\\in \\{0, 1\\}$. Then, she communicates with Bob about the measurement result through a classical channel.\n",
"4. Bob operates different gates on his qubit depending on Alice's measurement result. If, $m_A=0$ Bob acts $U_{B0}$ on his qubit; If $m_A = 1$, then Bob acts $U_{B1}$. Then, Bob measures his qubit and obtain $m_B \\in \\{0,1\\}$. **Note**: Both $U_{B0}$ and $U_{B1}$ are universal single-qubit gate `u3()`.\n",
"5. Calculate the loss function $L = p_{\\lvert\\psi\\rangle\\_01}+p_{\\lvert\\psi\\rangle\\_11}+ p_{\\lvert\\phi\\rangle\\_10}+ p_{\\lvert\\phi\\rangle\\_00}$, and use gradient-based optimization methods to minimize it.\n",
"6. Repeat 1-5 until the loss function converges.\n",
"7. Input the pre-shared state $\\lvert\\varphi\\rangle$ to make a decision and compare with Charlie's answer.\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulation with Paddle Quantum \n",
"\n",
"First, import relevant packages."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:44:41.820485Z",
"start_time": "2021-01-27T09:44:35.116796Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.stats import unitary_group\n",
"import paddle.fluid as fluid\n",
"from paddle_quantum.locc import LoccNet"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Randomly generate two orthogonal pure states $\\lvert\\psi\\rangle$ and $\\lvert\\phi\\rangle$ by Charlie."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:44:41.851407Z",
"start_time": "2021-01-27T09:44:41.823614Z"
}
},
"outputs": [],
"source": [
"def states_orthogonal_random(n, num=2):\n",
" \n",
" # Randomly generate two orthogonal states\n",
" assert num <= 2 ** n, \"return too many orthognal states\"\n",
" U = unitary_group.rvs(2 ** n)\n",
" return_list = []\n",
" for i in range(num):\n",
" return_list.append(U[i])\n",
" return return_list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below is the main part of our LOCC protocol:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:44:41.961552Z",
"start_time": "2021-01-27T09:44:41.880659Z"
}
},
"outputs": [],
"source": [
"class Net (LoccNet):\n",
" def __init__(self):\n",
" super(Net, self).__init__()\n",
" \n",
" # Add the first party Alice \n",
" # The first parameter 1 stands for how many qubits A holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(1, party_name='Alice')\n",
" \n",
" # Add the first party Bob \n",
" # The first parameter 1 stands for how many qubits B holds\n",
" # The second parameter records the name of this qubit holder\n",
" self.add_new_party(1, party_name='Bob')\n",
" \n",
" # Initialize Alice's parameter\n",
" self.theta1 = self.create_parameter(shape=[3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, seed=SEED),\n",
" dtype=\"float64\")\n",
" # Initialize Bob's parameter\n",
" # Bob has to prepare two circuits according Alice's measurement result \n",
" self.theta2 = self.create_parameter(shape=[3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, seed=SEED),\n",
" dtype=\"float64\")\n",
" self.theta3 = self.create_parameter(shape=[3], attr=fluid.initializer.Uniform(low=0.0, high=2 * np.pi, seed=SEED),\n",
" dtype=\"float64\")\n",
" \n",
" # Rewrite the input states into density martices\n",
" _states = states_orthogonal_random(2)\n",
" _states = [fluid.dygraph.to_variable(np.outer(init_state, init_state.conjugate())) for init_state in _states]\n",
" \n",
" # Initialize the system by distributing states\n",
" self.set_init_status(_states[0], [('Alice', 0), ('Bob', 0)])\n",
" self.psi = self.init_status\n",
" self.phi = self.reset_state(self.init_status, _states[1], [('Alice', 0), ('Bob', 0)])\n",
"\n",
" def A_circuit(self, theta, state, res):\n",
" # Alice's local operations\n",
" cir = self.create_ansatz('Alice')\n",
" # Add single-qubit universal gate\n",
" cir.u3(*theta, 0)\n",
" # Run circuit\n",
" after_state = cir.run(state)\n",
" # Measure the circuit and record the measurement results \n",
" after_state = self.measure(status=after_state, which_qubits=('Alice', 0), results_desired=res)\n",
" return after_state\n",
"\n",
" def B_circuit(self, theta, state, res):\n",
" # Bob's local operations\n",
" cir = self.create_ansatz('Bob')\n",
" # Add single-qubit universal gate\n",
" cir.u3(*theta, 0)\n",
" # Run circuit\n",
" after_state = cir.run(state)\n",
" # Measure the circuit and record the measurement results \n",
" after_state = self.measure(status=after_state, which_qubits=('Bob', 0), results_desired=res)\n",
" return after_state\n",
" \n",
" def forward(self):\n",
" # Training steps\n",
" # Quantum state after Alice's operation\n",
" phi = self.A_circuit(theta=self.theta1, state=self.phi, res=['0', '1'])\n",
" psi = self.A_circuit(theta=self.theta1, state=self.psi, res=['0', '1'])\n",
"\n",
" # Calculate the loss function\n",
" loss = 0\n",
" for each_phi in phi:\n",
" if each_phi.measured_result == '0':\n",
" # maybe we should call it phi_1 which means predicting the phi to psi\n",
" phi_1 = self.B_circuit(self.theta2, state=each_phi, res='1')\n",
" loss += phi_1.prob\n",
" elif each_phi.measured_result == '1':\n",
" psi_1 = self.B_circuit(self.theta3, state=each_phi, res='1')\n",
" loss += psi_1.prob\n",
"\n",
" for each_psi in psi:\n",
" if each_psi.measured_result == '0':\n",
" phi_0 = self.B_circuit(self.theta2, state=each_psi, res='0')\n",
" loss += phi_0.prob\n",
" elif each_psi.measured_result == '1':\n",
" psi_0 = self.B_circuit(self.theta3, state=each_psi, res='0')\n",
" loss += psi_0.prob\n",
"\n",
" return loss\n",
"\n",
" def evaluate(self):\n",
" # Test step\n",
" choice = np.random.choice(['phi', 'psi'])\n",
" if choice == 'phi':\n",
" self.status = self.phi\n",
" else:\n",
" self.status = self.psi\n",
" print('Charlie chooses the state', choice)\n",
"\n",
" # Alice's operations\n",
" status = self.A_circuit(theta=self.theta1, state=self.status, res=['0', '1'])\n",
" # Bob's operations \n",
" result_0 = list()\n",
" result_1 = list()\n",
" for each_status in status:\n",
" if each_status.measured_result == '0':\n",
" phi = self.B_circuit(theta=self.theta2, state=each_status, res=['0', '1'])\n",
" result_0.append(phi[0].prob.numpy())\n",
" result_0.append(phi[1].prob.numpy())\n",
"\n",
" elif each_status.measured_result == '1':\n",
" psi = self.B_circuit(theta=self.theta3, state=each_status, res=['0', '1'])\n",
" result_1.append(psi[0].prob.numpy())\n",
" result_1.append(psi[1].prob.numpy())\n",
" print(\"The probability that Alice and Bob recognize it as phi:\", result_0[0][0] + result_1[0][0])\n",
" print(\"The probability that Alice and Bob recognize it as psi:\", result_0[1][0] + result_1[1][0])\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Train the QNN parameters, and Charlie randomly select one of the two orthogonal states $\\lvert\\psi\\rangle$ and $\\lvert\\phi\\rangle$ and see whether Alice and Bob can distinguish it correctly."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2021-01-27T09:44:54.808382Z",
"start_time": "2021-01-27T09:44:41.997674Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"itr 0: 0.41451205406020003\n",
"itr 10: 0.028321852355736393\n",
"itr 20: 0.03680755589726499\n",
"itr 30: 0.020398244027092\n",
"itr 40: 0.00819374481825746\n",
"itr 50: 0.00456637111735521\n",
"itr 60: 0.00241819672311186\n",
"itr 70: 0.0010470019789628306\n",
"itr 80: 0.0004067586873393533\n",
"itr 90: 0.0001263131225914237\n",
"Minimum loss: 3.33357875113083e-05\n",
"======================== test stage ===============================\n",
"Charlie chooses the state psi\n",
"The probability that Alice and Bob recognize it as phi: 2.1562924160375818e-05\n",
"The probability that Alice and Bob recognize it as psi: 0.9999784370758396\n",
"Charlie chooses the state phi\n",
"The probability that Alice and Bob recognize it as phi: 0.9999920892819907\n",
"The probability that Alice and Bob recognize it as psi: 7.91071800851656e-06\n"
]
}
],
"source": [
"ITR = 100 # Set the number of training iterations\n",
"LR = 0.1 # Set learning rate\n",
"SEED = 999 # Fix randome seed for parameters in PQC\n",
"np.random.seed(999)\n",
"\n",
"\n",
"# Initialize dynamic graph mode\n",
"with fluid.dygraph.guard():\n",
" net = Net()\n",
" opt = fluid.optimizer.AdamOptimizer(learning_rate=LR, parameter_list=net.parameters())\n",
" loss_list = list()\n",
" # Train the LOCC net for ITR iterations by gradient descent\n",
" for itr in range(ITR):\n",
" loss = net()\n",
" loss.backward()\n",
" opt.minimize(loss)\n",
" net.clear_gradients()\n",
" loss_list.append(loss.numpy()[0])\n",
" if itr % 10 == 0:\n",
" print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
" print(\"Minimum loss:\", loss_list[-1])\n",
" \n",
" print(\"======================== test stage ===============================\")\n",
" np.random.seed(10)\n",
" net.evaluate()\n",
" np.random.seed(6)\n",
" net.evaluate()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"It can be seen from the simulation results that the trained quantum circuit can distinguish two orthogonal quantum states almost perfectly with an accuracy $>99.9\\%$. There is an interesting question that can we generalize this discrimination scheme by adding more states to the category."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## References\n",
"\n",
"[1] Barnett, Stephen M., and Sarah Croke. \"Quantum state discrimination.\" [Advances in Optics and Photonics 1.2 (2009): 238-278.](https://www.osapublishing.org/abstract.cfm?id=176580)\n",
"\n",
"[2] Chefles, Anthony. \"Quantum state discrimination.\" [Contemporary Physics 41.6 (2000): 401-424.](https://arxiv.org/abs/quant-ph/0010114)\n",
"\n",
"[3] Walgate, Jonathan, et al. \"Local distinguishability of multipartite orthogonal quantum states.\" [Physical Review Letters 85.23 (2000): 4972.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.85.4972)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 5
}
......@@ -4561,7 +4561,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.8"
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
......
......@@ -79,7 +79,7 @@
"output_type": "stream",
"text": [
"Random Hamiltonian in Pauli string format = \n",
" [[0.2053246312210153, 'y1'], [0.8007921371055502, 'y0'], [-0.005452633476696667, 'y1'], [0.6584698440348358, 'z0'], [0.8231741294360668, 'y0,x1'], [-0.9500264430442884, 'z1,z0'], [0.500344392541574, 'x1,z0'], [0.26774681450914084, 'z1'], [-0.6560070288135618, 'y0,z1'], [-0.24860662455587024, 'x1']]\n"
" [[-0.12086078882948925, 'z0,y1'], [0.8520481243020479, 'x0'], [0.7311915310296655, 'x1'], [0.8878180549783599, 'x1'], [-0.07152647722934247, 'z0'], [0.36890035805078325, 'z1,z0'], [-0.47650461185021764, 'z1,y0'], [0.957686278969355, 'z0,y1'], [-0.4742145513250544, 'y0'], [-0.30811279345705755, 'x1']]\n"
]
}
],
......@@ -124,7 +124,7 @@
" cir = UAnsatz(N)\n",
" \n",
" # 调用内置的量子神经网络模板\n",
" cir.universal_2_qubit_gate(theta)\n",
" cir.universal_2_qubit_gate(theta, [0, 1])\n",
"\n",
" # 返回量子神经网络所模拟的酉矩阵 U\n",
" return cir.U"
......@@ -239,11 +239,11 @@
"name": "stdout",
"output_type": "stream",
"text": [
"iter: 10 loss: -5.3321\n",
"iter: 20 loss: -7.4093\n",
"iter: 30 loss: -7.6601\n",
"iter: 40 loss: -7.8402\n",
"iter: 50 loss: -8.0516\n"
"iter: 10 loss: -6.1446\n",
"iter: 20 loss: -7.7827\n",
"iter: 30 loss: -8.0978\n",
"iter: 40 loss: -8.4615\n",
"iter: 50 loss: -8.4589\n"
]
}
],
......@@ -303,14 +303,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"The estimated ground state energy is: [-2.89029006]\n",
"The theoretical ground state energy: -2.914376077505768\n",
"The estimated 1st excited state energy is: [-0.05679422]\n",
"The theoretical 1st excited state energy: -0.07729623846713303\n",
"The estimated 2nd excited state energy is: [0.73281967]\n",
"The theoretical 2nd excited state energy: 0.7671454582095175\n",
"The estimated 3rd excited state energy is: [2.21426461]\n",
"The theoretical 3rd excited state energy: 2.2245268577633834\n"
"The estimated ground state energy is: [-2.3665305]\n",
"The theoretical ground state energy: -2.385744737010799\n",
"The estimated 1st excited state energy is: [-1.34818389]\n",
"The theoretical 1st excited state energy: -1.3356376227295346\n",
"The estimated 2nd excited state energy is: [1.337023]\n",
"The theoretical 2nd excited state energy: 1.3356376227295335\n",
"The estimated 3rd excited state energy is: [2.37769139]\n",
"The theoretical 3rd excited state energy: 2.3857447370107985\n"
]
}
],
......@@ -365,7 +365,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.10"
"version": "3.7.0"
},
"toc": {
"base_numbering": 1,
......
......@@ -78,7 +78,7 @@
"output_type": "stream",
"text": [
"Random Hamiltonian in Pauli string format = \n",
" [[0.5288681247649314, 'z1'], [0.9258545165930183, 'z0,z1'], [-0.2323659299902321, 'x0,z1'], [-0.9558751117603583, 'z1'], [-0.619522452161382, 'x1'], [-0.08249108590273635, 'x1,z0'], [0.1662238964776901, 'z0,z1'], [0.3582576655826999, 'x0,x1'], [-0.5987469327681758, 'z1'], [0.4023148129799976, 'x0,y1']]\n"
" [[0.5607206331858847, 'z0'], [-0.9931668020533528, 'z1'], [-0.6264392463826307, 'x1,x0'], [-0.8167732501071665, 'x1,x0'], [0.6792869149087406, 'y0'], [-0.8907888336706506, 'z1,y0'], [0.7878221587000573, 'y1'], [-0.9925516732999857, 'z0,y1'], [-0.7987244836834173, 'x1'], [-0.3124885350069655, 'z0']]\n"
]
}
],
......@@ -123,7 +123,7 @@
" cir = UAnsatz(N)\n",
" \n",
" # Call the built-in quantum neural network template\n",
" cir.universal_2_qubit_gate(theta)\n",
" cir.universal_2_qubit_gate(theta, [0, 1])\n",
"\n",
" # Return the unitary matrix U simulated by the quantum neural network\n",
" return cir.U"
......@@ -240,11 +240,11 @@
"name": "stdout",
"output_type": "stream",
"text": [
"iter: 10 loss: -6.6361\n",
"iter: 20 loss: -7.2810\n",
"iter: 30 loss: -7.5451\n",
"iter: 40 loss: -7.6850\n",
"iter: 50 loss: -7.7028\n"
"iter: 10 loss: -8.9318\n",
"iter: 20 loss: -10.5747\n",
"iter: 30 loss: -11.0288\n",
"iter: 40 loss: -11.2393\n",
"iter: 50 loss: -11.3251\n"
]
}
],
......@@ -304,14 +304,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"The estimated ground state energy is: [-2.32236871]\n",
"The theoretical ground state energy: -2.325787163439509\n",
"The estimated 1st excited state energy is: [-0.73748569]\n",
"The theoretical 1st excited state energy: -0.7415874007735302\n",
"The estimated 2nd excited state energy is: [0.73927594]\n",
"The theoretical 2nd excited state energy: 0.7415874007735306\n",
"The estimated 3rd excited state energy is: [2.32057845]\n",
"The theoretical 3rd excited state energy: 2.325787163439509\n"
"The estimated ground state energy is: [-3.73602114]\n",
"The theoretical ground state energy: -3.753326573918093\n",
"The estimated 1st excited state energy is: [-0.80121753]\n",
"The theoretical 1st excited state energy: -0.8299424429462511\n",
"The estimated 2nd excited state energy is: [1.48536538]\n",
"The theoretical 2nd excited state energy: 1.5045893361511813\n",
"The estimated 3rd excited state energy is: [3.05187329]\n",
"The theoretical 3rd excited state energy: 3.078679680713162\n"
]
}
],
......@@ -366,7 +366,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.10"
"version": "3.7.0"
},
"toc": {
"base_numbering": 1,
......
......@@ -15,7 +15,7 @@
"source": [
"## Overview\n",
"\n",
"It is widely believed that one of the most promising applications of quantum computing in the near future is solving quantum chemistry problems [1-2]. **Variational Quantum Eigensolver** (VQE) is a strong proof to this possibility of studying quantum chemistry with **Noisy Intermediate-Scale Quantum** (NISQ) devices [1-4]. The core task is to solve the energy ground state of any molecular Hamiltonian $\\hat{H}$ by preparing a parametrized wave function ansatz $|\\Psi(\\boldsymbol\\theta)\\rangle$ on a quantum computer and adopt classical optimization methods (e.g. gradient descent) to adjust the parameters $\\boldsymbol\\theta$ to minimize the expectation value $\\langle \\Psi(\\boldsymbol\\theta)|\\hat{H}|\\Psi(\\boldsymbol\\theta)\\rangle$. This approach is based on the **Rayleigh-Ritz variational principle**. \n",
"It is widely believed that one of the most promising applications of quantum computing in the near future is solving quantum chemistry problems [1-2]. **Variational Quantum Eigensolver** (VQE) is a strong proof to this possibility of studying quantum chemistry with **Noisy Intermediate-Scale Quantum** (NISQ) devices [1-4]. The core task is to solve the ground state of any molecular Hamiltonian $\\hat{H}$ by preparing a parametrized wave function ansatz $|\\Psi(\\boldsymbol\\theta)\\rangle$ on a quantum computer and adopt classical optimization methods (e.g. gradient descent) to adjust the parameters $\\boldsymbol\\theta$ to minimize the expectation value $\\langle \\Psi(\\boldsymbol\\theta)|\\hat{H}|\\Psi(\\boldsymbol\\theta)\\rangle$. This approach is based on the **Rayleigh-Ritz variational principle**. \n",
"\n",
"$$\n",
"E_0 = \\min_{\\boldsymbol\\theta} \\langle \\Psi(\\boldsymbol\\theta)|\\hat{H}|\\Psi(\\boldsymbol\\theta)\\rangle.\n",
......@@ -88,7 +88,7 @@
"\n",
"\n",
"\n",
"**Note:** A detailed review on quantum chemistry and existing classical computational methods are far beyond the scope of this tutorial, we refer the enthusiastic readers to the standard textbooks *'Molecular Electronic-Structure Theory'* [5] by Helgaker and *'Modern Quantum Chemistry: Introduction to Advanced Electronic Structure Theory'* [7] by Szabo & Ostlund. To bridge to knowledge gap between quantum chemistry and quantum computing, please check the following review papers [Quantum computational chemistry](https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.92.015003) [2] and [Quantum chemistry in the age of quantum computing](https://pubs.acs.org/doi/10.1021/acs.chemrev.8b00803) [1].\n",
"**Note:** A detailed review on quantum chemistry and existing classical computational methods are far beyond the scope of this tutorial, we refer the enthusiastic readers to the standard textbooks *'Molecular Electronic-Structure Theory'* [5] by Helgaker and *'Modern Quantum Chemistry: Introduction to Advanced Electronic Structure Theory'* [7] by Szabo & Ostlund. To bridge to knowledge gap between quantum chemistry and quantum computing, please check the following review papers [Quantum computational chemistry](https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.92.015003) [2] and [Quantum Chemistry in the Age of Quantum Computing](https://pubs.acs.org/doi/10.1021/acs.chemrev.8b00803) [1].\n",
"\n",
"**Note:** For energy calculation, it is desired to reach the **chemical accuracy** of $1.6\\times10^{-3}$ Hartree or 1 kcal/mol . "
]
......@@ -97,7 +97,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Energy ground state of the hydrogen molecule $H_2$\n",
"## Ground state of the hydrogen molecule $H_2$\n",
"\n",
"### Building electronic Hamiltonian\n",
"\n",
......@@ -1834,7 +1834,7 @@
"\n",
"## References\n",
"\n",
"[1] [Cao, Yudong, et al. Quantum chemistry in the age of quantum computing. Chemical reviews 119.19 (2019): 10856-10915.](https://pubs.acs.org/doi/10.1021/acs.chemrev.8b00803)\n",
"[1] [Cao, Yudong, et al. Quantum Chemistry in the Age of Quantum Computing. Chemical reviews 119.19 (2019): 10856-10915.](https://pubs.acs.org/doi/10.1021/acs.chemrev.8b00803)\n",
"\n",
"[2] [McArdle, Sam, et al. Quantum computational chemistry. Reviews of Modern Physics 92.1 (2020): 015003.](https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.92.015003)\n",
"\n",
......@@ -1868,7 +1868,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
"version": "3.7.9"
},
"toc": {
"base_numbering": 1,
......
......@@ -59,8 +59,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[ 0.2569+0.j -0.012 +0.0435j -0.0492-0.0055j -0.0548+0.0682j]\n",
" [-0.012 -0.0435j 0.2959-0.j 0.1061-0.0713j -0.0392-0.0971j]\n",
"[[ 0.2569-0.j -0.012 +0.0435j -0.0492-0.0055j -0.0548+0.0682j]\n",
" [-0.012 -0.0435j 0.2959+0.j 0.1061-0.0713j -0.0392-0.0971j]\n",
" [-0.0492+0.0055j 0.1061+0.0713j 0.2145-0.j 0.0294-0.1132j]\n",
" [-0.0548-0.0682j -0.0392+0.0971j 0.0294+0.1132j 0.2327+0.j ]]\n"
]
......@@ -110,7 +110,7 @@
" # 按照量子比特数量/网络宽度初始化量子神经网络\n",
" cir = UAnsatz(N)\n",
" # 调用内置的量子神经网络模板\n",
" cir.universal_2_qubit_gate(theta)\n",
" cir.universal_2_qubit_gate(theta, [0, 1])\n",
" # 返回量子神经网络所模拟的酉矩阵 U\n",
" return cir.U"
]
......@@ -322,7 +322,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.10"
"version": "3.7.0"
},
"toc": {
"base_numbering": 1,
......
......@@ -65,8 +65,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[ 0.2569+0.j -0.012 +0.0435j -0.0492-0.0055j -0.0548+0.0682j]\n",
" [-0.012 -0.0435j 0.2959-0.j 0.1061-0.0713j -0.0392-0.0971j]\n",
"[[ 0.2569-0.j -0.012 +0.0435j -0.0492-0.0055j -0.0548+0.0682j]\n",
" [-0.012 -0.0435j 0.2959+0.j 0.1061-0.0713j -0.0392-0.0971j]\n",
" [-0.0492+0.0055j 0.1061+0.0713j 0.2145-0.j 0.0294-0.1132j]\n",
" [-0.0548-0.0682j -0.0392+0.0971j 0.0294+0.1132j 0.2327+0.j ]]\n"
]
......@@ -115,7 +115,7 @@
" # Initialize the quantum neural network according to the number of qubits/network width\n",
" cir = UAnsatz(N)\n",
" # Call the built-in quantum neural network template\n",
" cir.universal_2_qubit_gate(theta)\n",
" cir.universal_2_qubit_gate(theta, [0, 1])\n",
" # Return the unitary matrix U simulated by the quantum neural network\n",
" return cir.U"
]
......@@ -337,7 +337,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.10"
"version": "3.7.0"
},
"toc": {
"base_numbering": 1,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册