多量子比特与纠缠态
单个量子比特很有趣,但单独来看,它们并没有提供计算优势。我们现在将研究如何表示多个量子比特,以及这些量子比特如何相互作用。我们已经知道如何用二维矢量来表示一个量子比特的状态,现在我们将看到如何表示多个量子比特的状态。
1. 表示多量子比特状态
我们看到一个比特有两种可能的状态,而一个量子比特状态有两个复振幅。同样地,两个比特有四种可能的状态:
00 01 10 11
而要描述两个量子比特的状态需要四个数振幅。我们将这些振幅存储在类似这样的四维向量中:
测量规则仍然以同样的方式工作:
而同样的含义也是成立的,比如说归一化条件:
如果我们有两个分离的量子比特,我们可以用kronecker积来描述它们的集体状态:
而按照同样的规则,我们可以用Kronecker积来描述任何数量的量子比特的集体状态。下面是一个有三个量子比特的例子:
如果我们有 \boldsymbol{n} 个量子比特,我们将需要跟踪 \boldsymbol{2^n} 个复振幅。正如我们所看到的,这些向量随着量子比特的数量呈指数增长。这就是拥有大量量子比特的量子计算机如此难以模拟的原因。一台现代笔记本电脑可以很容易地模拟大约20个量子比特的一般量子态,但模拟100个量子比特对于最大的超级计算机来说非常难。
让我们来看一个示例电路:
from qiskit import QuantumCircuit, Aer, assemble
import numpy as np
from qiskit.visualization import plot_histogram, plot_bloch_multivector
qc = QuantumCircuit(3)
# Apply H-gate to each qubit:
for qubit in range(3):
qc.h(qubit)
# See the circuit:
qc.draw()
每个量子比特都处于状态 \ket{+} ,所以我们应该看到这个向量:
# Let's see the result
svsim = Aer.get_backend('aer_simulator')
qc.save_statevector()
qobj = assemble(qc)
final_state = svsim.run(qobj).result().get_statevector()
# In Jupyter Notebooks we can display this nicely using Latex.
# If not using Jupyter Notebooks you may need to remove the
# array_to_latex function and use print(final_state) instead.
from qiskit.visualization import array_to_latex
array_to_latex(final_state, prefix="\\text{Statevector} = ")
我们得到了预期的结果。
1.2 快速练习:
- 写下这些量子比特的kronecker积:
a) \ket{0}\ket{1}
b) \ket{0}\ket{+}
c) \ket{+}\ket{1}
d) \ket{-}\ket{+}
- 把状态 \ket{\psi} = \frac{1}{\sqrt{2}}\ket{00} + \frac{\boldsymbol{i}}{\sqrt{2}}\ket{01} 写成两个独立的量子比特。
2. 基于多量子比特状态矢量的单量子比特门
我们已经知道,一个X门是由矩阵来表示的:
并且它对状态 \ket{0} 的作用如下:
但可能不清楚X门如何作用于多量子比特向量中的一个量子比特。幸运的是,这个规则非常简单;就像我们用kronecker积来计算多量子比特状态向量一样,我们用张量积来计算作用于这些状态向量的矩阵。例如,在下面的电路中:
qc = QuantumCircuit(2)
qc.h(0)
qc.x(1)
qc.draw()
我们可以用它们的kronecker积来表示同时进行的运算(H和X):
该操作看起来是这样的:
然后我们可以将其应用于我们的四维状态向量 \ket{q_1q_0} 。这可能会变得相当混乱,你会经常看到更清晰的符号:
我们可以使用Qiskit的aer_simulator
来代替我们手工计算。Aer模拟器将我们电路中的所有门相乘,编译出执行整个量子电路的单个酉矩阵:
usim = Aer.get_backend('aer_simulator')
qc.save_unitary()
qobj = assemble(qc)
unitary = usim.run(qobj).result().get_unitary()
并查看结果:
# In Jupyter Notebooks we can display this nicely using Latex.
# If not using Jupyter Notebooks you may need to remove the
# array_to_latex function and use print(unitary) instead.
from qiskit.visualization import array_to_latex
array_to_latex(unitary, prefix="\\text{Circuit = }\n")
如果我们想一次只对一个量子比特应用一个门(比如下面的电路),我们就用kronecker积与单位矩阵来描述,例如:
qc = QuantumCircuit(2)
qc.x(1)
qc.draw()
# Simulate the unitary
usim = Aer.get_backend('aer_simulator')
qc.save_unitary()
qobj = assemble(qc)
unitary = usim.run(qobj).result().get_unitary()
# Display the results:
array_to_latex(unitary, prefix="\\text{Circuit = } ")
我们可以看到Qiskit已经执行了kronecker积:
2.1 快速练习:
-
计算矩阵U=XZH。使用Qiskit的Aer模拟器来检查你的结果。
-
尝试改变上述电路中的门。计算它们的kronecker积,然后用Aer模拟器检查你的答案。
注意:
不同的书籍、软件和网站对其量子比特的排序是不同的。这意味着同一电路的kronecker积可能看起来非常不同。在查阅其他资料时,请尽量记住这一点。
3. 多量子比特门
现在我们知道了如何表示多个量子比特的状态,我们现在准备学习量子比特之间是如何相互作用的。一个重要的双量子比特门是CNOT门。
3.1 CNOT门
你曾经在1.2节中见过这个门。这个门是一个条件门,如果第一个量子比特(控制)的状态为 \ket{1} ,则在第二个量子比特(目标)上执行一个X门。该门的电路是这样画的,q0为控制,q1为目标。
qc = QuantumCircuit(2)
# Apply CNOT
qc.cx(0,1)
# See the circuit:
qc.draw()
当我们的量子比特不处于 \ket{0} 或 \ket{1} (表现为经典比特)的叠加态时,这个门可以非常简单直观地理解。我们可以使用经典真值表:
Input(t,c) | Ouput(t,c) |
---|---|
00 | 00 |
01 | 11 |
10 | 10 |
11 | 01 |
当作用在我们的四维状态向量上,它是下列两个矩阵之一:
取决于哪个量子比特是控制,哪个是目标。不同的参考书、模拟器和论文均以不同的方式排列其量子比特。在我们的例子中,左边的矩阵对应于上述电路中的CNOT。这个矩阵交换了我们的状态向量中 \ket{01} 和 \ket{11} 的振幅:
我们已经看到它是如何作用于经典状态的,但现在让我们看看它是如何作用于叠加态的量子比特的。我们将把一个量子比特置于以下状态 \ket{+} :
qc = QuantumCircuit(2)
# Apply H-gate to the first:
qc.h(0)
qc.draw()
# Let's see the result:
svsim = Aer.get_backend('aer_simulator')
qc.save_statevector()
qobj = assemble(qc)
final_state = svsim.run(qobj).result().get_statevector()
# Print the statevector neatly:
array_to_latex(final_state, prefix="\\text{Statevector = }")
正如预期的那样,这产生了状态 \ket{0} \otimes \ket{+} = \ket{0+} :
让我们看看当我们应用CNOT门时会发生什么:
qc = QuantumCircuit(2)
# Apply H-gate to the first:
qc.h(0)
# Apply a CNOT:
qc.cx(0,1)
qc.draw()
# Let's get the result:
qc.save_statevector()
qobj = assemble(qc)
result = svsim.run(qobj).result()
# Print the statevector neatly:
final_state = result.get_statevector()
array_to_latex(final_state, prefix="\\text{Statevector = }")
我们看到我们有状态:
这种状态对我们来说非常有趣,因为它是纠缠的。这就把我们引向下一节。
3.2 纠缠态
在上一节中,我们可以创建状态:
这就是所谓的Bell态。我们可以看到,这个状态有50%的概率被测量在状态|00⟩,有50%的概率被测量在状态|11⟩。最有趣的是,它被测量在|01⟩或|10⟩状态下的几率为0。我们可以在Qiskit中看到:
plot_histogram(result.get_counts())
这种组合状态不能被写成两个独立的量子比特状态,这具有有趣的含义。尽管我们的量子比特处于叠加态,但测量一个量子比特会告诉我们另一个量子比特的状态,并使其坍缩。例如,如果我们测量量子比特 q_0 并得到状态 \ket{1} ,我们的量子比特的集体状态就会发生这样的变化:
即使我们将这些量子比特分离在光年之外,测量一个量子比特也会使叠加效应崩溃,并似乎对另一个量子比特产生立即的影响。这就是在20世纪初令许多物理学家感到不安的"鬼魅般的超距作用"。
值得注意的是,测量结果是随机的,一个量子比特的测量统计不受另一个量子比特上任何操作的影响。正因为如此,没有办法利用共享量子态进行通信。这就是所谓的无通信定理。[1]
3.3 可视化纠缠态
我们已经看到,这种状态不能被写成两个独立的量子比特状态,这也意味着当我们试图在独立的布洛赫球面上绘制状态时,我们会丢失信息。
plot_bloch_multivector(final_state)
考虑到我们在前面的章节中是如何定义布洛赫球面的,可能并不清楚Qiskit是如何用这样的纠缠量子比特计算布洛赫矢量的。在单量子比特的情况下,布洛赫矢量沿某一轴线的位置很好地对应于在该基础上测量的期望值。如果我们将此作为绘制布洛赫矢量的规则,我们就会得出上述结论。这表明不存在保证特定测量的单比特测量基。这与我们的单量子比特态形成了对比,在单量子比特态中,我们总是可以选择一个单量子比特基。以这种方式观察单个量子比特,我们会忽略量子比特之间相关性的重要影响。我们无法区分不同的纠缠态。例如,这两种状态:
在这些独立的布洛赫球面上看起来都是一样的,尽管是非常不同的状态,有着不同的测量结果。
我们还能如何将这个状态向量可视化呢?这个状态向量只是四个振幅(复数)的集合,我们有无穷无尽的方法可以将其映射到图像上。其中一种可视化方法是Q球面,这种方法每个振幅由球体表面的一个圆球表示。圆球的大小与振幅的大小成正比,而颜色与振幅的相位成正比。以下的振幅 \ket{00} 和 \ket{11} 是相等的,所有其他振幅都是0。
from qiskit.visualization import plot_state_qsphere
plot_state_qsphere(final_state)
从这里我们可以清楚地看到量子比特之间的相关性。Q球面的形状没有任何意义,它只是一种组织我们的圆球的好方法;状态中0的数量与状态在Z轴上的位置成正比,所以这里我们可以看到 \ket{00} 的振幅位于球体的最上方,而 \ket{11} 的振幅位于球体的最下方。
3.4 练习
-
创建一个能产生Bell态 \tfrac{1}{\sqrt{2}}(\ket{01} + \ket{10}) 的量子电路。使用状态向量模拟器来验证你的结果。
-
你在第1题中创建的电路将状态 \ket{00} 转换为 \tfrac{1}{\sqrt{2}}(\ket{01} + \ket{10}) ,使用Qiskit的模拟器计算这个电路的酉矩阵。验证这个酉矩阵是否真的执行了正确的转换。
-
想一想你可以用什么方式来直观地表示一个状态向量。你能设计一种有趣的可视化方式,可以从中读出每个振幅的大小和相位吗?
4. 参考文献
[1] Asher Peres, Daniel R. Terno, Quantum Information and Relativity Theory, 2004, [quant-ph/0212023] Quantum Information and Relativity Theory
import qiskit.tools.jupyter
%qiskit_version_table
版本信息
Qiskit Software | Version |
---|---|
Qiskit | 0.27.0 |
Terra | 0.17.4 |
Aer | 0.8.2 |
Ignis | 0.6.0 |
Aqua | 0.9.2 |
IBM Q Provider | 0.14.0 |
System information | |
---|---|
Python | 3.7.7 (default, May 6 2020, 04:59:01) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 8 |
Memory (Gb) | 32.0 |
Thu Jun 17 15:13:01 2021 BST |