本教程演示如何编写 Q# 程序来操作和测量量子比特并演示叠加和纠缠效果。
经典位保存单个二进制值(如 0 或 1),而
量子比特
的状态则可以是 0 和 1 的双量子状态的
叠加
。 每种可能的量子状态具有一个关联的概率幅度。
测量
量子比特的行为生成具有特定概率的二进制结果 0 或 1,并更改量子比特的叠加状态。
多个量子比特可以
纠缠
,使得它们不能相互独立地描述。 也就是说,无论纠缠对中的一个量子比特发生什么情况,另一个量子比特也会发生相同的情况。
在本教程中,你将准备两个处于特定量子状态的量子比特来了解如何通过 Q# 运算量子比特来更改其状态并演示叠加和量子比特纠缠效果。 你将逐步生成 Q# 程序以了解量子比特状态、运算和测量。
若要完成本教程,你需要:
具有活动订阅的 Azure 帐户。 如果没有 Azure 帐户,请免费注册并注册
即用即付订阅
。
Azure Quantum 工作区。 有关详细信息,请参阅
创建 Azure Quantum 工作区
。
本教程介绍以下操作:
创建 Q# 运算以测量量子比特并将其初始化为所需状态。
创建量子比特并测试程序。
将量子比特置于叠加状态。
纠缠一对量子比特。
在工作区中创建一个新笔记本
登录到
Azure 门户
,并选择在上一步中创建的工作区。
在左侧边栏选项卡中,选择“笔记本”。
单击“我的笔记本”,然后单击“新增”。
在“内核类型”中选择“IQ#”。
键入文件的名称(例如 Entanglement.ipynb),然后单击“创建文件”。
当你的新笔记本打开时,它会根据订阅和工作区信息自动为第一个单元格创建代码。
%azure.connect "/subscriptions/\<subscription ID>/\<resource group>/providers/Microsoft.Quantum/Workspaces/\<workspace>" \<location>
%azure.connect 是一个 IQ# magic 命令,它是帮助简化 Jupyter Notebook 中的任务的一组命令。
如果运行此单元,它应该对你的订阅进行身份验证并显示可用提供商及其目标的列表。
使用测量初始化量子比特
第一步是定义一个 Q# 运算,用于将量子比特初始化为已知状态。 可以调用此运算以将量子比特设置为经典状态,这意味着它要么在 100% 的时间返回 Zero
,要么在 100% 的时间返回 One
。 Zero
和 One
是表示对量子比特进行测量时仅有的两个可能结果的 Q# 值。
单击“+代码”添加新的单元并添加以下代码:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
Microsoft.Quantum.Intrinsic
和 Microsoft.Quantum.Canon
命名空间由此代码中的运算使用,将在 Azure Quantum 笔记本的每个单元中自动打开。
该代码示例引入了用于变换量子比特状态的两个标准运算(M
和 X
)。
SetQubitState
运算:
采用两个参数:一个名为 desired
的类型 Result
,表示量子比特的所需状态(0 或 1);类型 Qubit
。
执行测量运算 M
,以测量量子比特的状态(Zero
或 One
),并将结果与 desired
中指定的值进行比较。
如果测量结果与比较值不匹配,则运行 X
运算,以将量子比特的状态翻转为返回 Zero
和 One
的测量的概率反转位置。 这样,SetQubitState
会始终将目标量子比特置于所需状态下。
接下来,为了演示 SetQubitState
运算的效果,请创建名为 TestBellState
的另一个运算。
添加另一个新单元并添加以下代码:
operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones':
if resultQ1 == One {
set numOnesQ1 += 1;
if resultQ2 == One {
set numOnesQ2 += 1;
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Return number of |0> states, number of |1> states
Message("q1:Zero, One q2:Zero, One");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
TestBellState
运算:
采用两个参数:count
,即测量的运行次数;initial
,即要将量子比特初始化到的所需状态。
调用 use
语句以初始化两个量子比特。
循环 count
迭代。 对于每次循环,它将
- 调用
SetQubitState
以便对第一个量子比特设置指定的 initial
值。
- 再次调用
SetQubitState
以将第二个量子比特设置为 Zero
状态。
- 使用
M
运算来测量每个量子比特。
- 存储每个量子比特的、返回
One
的测量数目。
- 循环完成后,它会再次调用
SetQubitState
以将量子比特重置为已知状态 (Zero
),使其他对象能够以已知状态分配量子比特。 这是 use
语句所必需的。
- 最后,它使用
Message
函数在返回结果之前将消息显示在控制台中。
在继续执行叠加和纠缠过程之前,测试到目前为止的代码,以查看量子比特的初始化和测量。
若要运行 TestBellState
运算,请使用 %simulate
magic 命令调用 Azure Quantum 全状态模拟器。 需要指定 count
和 initial
参数,例如 count=1000
和 initial=1
。 这会将第一个量子比特初始化为 One
,并测量每个量子比特 1000 次。 使用以下命令添加一个新单元,然后单击“全部运行”:
%simulate TestBellState count=1000 initial=1
你应会看到以下输出:
q1:Zero, One q2:Zero, One
(0, 1000, 1000, 0)
由于尚未操作量子比特,因此它们保留了初始值:第一个量子比特每次返回 One
,第二个量子比特返回 Zero
。
如果再次使用 initial=0
运行该单元,则应会注意到,第一个量子比特每次也返回 Zero
。
%simulate TestBellState count=1000 initial=0
q1:Zero, One q2:Zero, One
(1000, 0, 1000, 0)
将量子比特置于叠加状态
目前,程序中的量子比特都处于经典状态,即,要么为 1,要么为 0。 之所以知道这一点,是因为该程序会将量子比特初始化为已知状态,并且你未添加任何过程来操作量子比特。 在纠缠量子比特之前,需要将第一个量子比特置于叠加状态,在此状态下,量子比特的测量会在 50% 的时间返回 Zero
,在 50% 的时间返回 One
。 从概念上讲,可将量子比特视为 Zero
与 One
之间所有状态的线性组合。
Q# 提供了 H
(Hadamard) 运算用于将量子比特置于叠加状态。 回顾前面的使用测量初始化量子比特过程中的 X
运算,它将量子比特从 0 翻转为 1(或反之);H
运算将量子比特中途翻转为 0 或 1 相等概率状态。 测量时,叠加的量子比特应返回大致相等数量的 Zero
和 One
结果。
在前一个具有 TestBellState
的单元中,在 for
循环内添加 H
运算:
for test in 1..count {
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1); // Add the H operation after initialization and before measurement
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
在 %simulate
命令中再次将量子比特初始化为 1,单击“全部运行”,然后可以看到第一个量子比特的叠加结果:
%simulate TestBellState count=1000 initial=1
q1:Zero, One q2:Zero, One
(523, 477, 1000, 0) // results will vary
每次运行该程序,第一个量子比特的结果略有不同,但在大约 50% 的时间为 One
,大约 50% 的时间为 Zero
,而第二个量子比特的结果始终保持为 Zero
。
Q1:Zero/One Q2:Zero/One
(510, 490, 1000, 0)
将第一个量子比特初始化为 Zero
会返回类似的结果。
%simulate TestBellState count=1000 initial=0
Q1:Zero/One Q2:Zero/One
(504, 496, 1000, 0)
纠缠两个量子比特
如前所述,纠缠的量子比特的连接方式使得它们不能相互独立地描述。 也就是说,无论纠缠对中的一个量子比特发生了什么运算,另一个量子比特也会发生相同的运算。 这样,只需测量一个量子比特的状态,就能知道另一个量子比特的最终状态,而无需测量它。 (此示例使用两个量子比特;但是,也可以纠缠三个或更多量子比特)。
Q# 提供了 CNOT
(Controlled-NOT 的缩写)运算用于实现纠缠。 对两个量子比特运行此操作的结果是,在第一个量子比特是 One
的情况下翻转第二个量子比特。
在 for
循环中紧接在 H
运算之后添加 CNOT
运算。 TestBellState
运算现在应如下所示:
operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1);
CNOT(q1, q2); // added CNOT operation
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones':
if resultQ1 == One {
set numOnesQ1 += 1;
if resultQ2 == One {
set numOnesQ2 += 1;
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Return number of |0> states, number of |1> states
Message("q1:Zero, One q2:Zero, One");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
单击“全部运行”以运行更新的运算,应会看到:
Q1:Zero/One Q2:Zero/One
(502, 498, 502, 498) // actual results will vary
第一个量子比特的统计信息没有变化(测量后返回 Zero
或 One
的概率各为50%),但第二个量子比特的测量结果始终与第一个量子比特的测量结果相同,而不管该量子比特初始化到了哪种状态。 CNOT
运算已将两个量子比特纠缠在一起,因此,无论其中一个量子比特发生了什么运算,另一个量子比特也会发生这种运算。
- 使用首选语言和开发环境安装 Quantum 开发工具包 (QDK)。
- 如果已安装 QDK,请确保将其更新至最新版本。
- 为 Q# 应用程序或 C# 宿主程序创建一个名为 Bell 的 Q# 项目。 或者,可以直接在 Juptyer Notebook 中或从 Python 宿主程序运行 Q# 代码。
本教程介绍以下操作:
- 创建 Q# 运算以测量量子比特并将其初始化为所需状态。
- 创建量子比特并测试程序。
- 将量子比特置于叠加状态。
- 纠缠一对量子比特。
使用测量初始化量子比特
第一步是定义一个 Q# 运算,用于将量子比特初始化为已知状态。 可以调用此运算以将量子比特设置为经典状态,这意味着它要么在 100% 的时间返回 Zero
,要么在 100% 的时间返回 One
。 Zero
和 One
是表示对量子比特进行测量时仅有的两个可能结果的 Q# 值。
在项目中,将 Program.qs
的内容替换为以下代码:
namespace Bell {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
该代码示例引入了用于变换量子比特状态的两个标准运算(M
和 X
)。
SetQubitState
运算:
- 采用两个参数:一个名为
desired
的类型 Result
,表示量子比特的所需状态(0 或 1);类型 Qubit
。
- 执行测量运算
M
,以测量量子比特的状态(Zero
或 One
),并将结果与 desired
中指定的值进行比较。
- 如果测量结果与比较值不匹配,则运行
X
运算,以将量子比特的状态翻转为返回 Zero
和 One
的测量的概率反转位置。 这样,SetQubitState
会始终将目标量子比特置于所需状态下。
接下来,为了演示 SetQubitState
运算的效果,请创建名为 TestBellState
的另一个运算。
将以下运算添加到 Program.qs
文件中的 SetQubitState
运算后面:
operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' we saw:
if resultQ1 == One {
set numOnesQ1 += 1;
if resultQ2 == One {
set numOnesQ2 += 1;
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Return times we saw |0>, times we saw |1>
Message("q1:Zero, One q2:Zero, One");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
TestBellState
运算:
- 采用两个参数:
count
,即测量的运行次数;initial
,即要将量子比特初始化到的所需状态。
- 调用
use
语句以初始化两个量子比特。
- 循环
count
迭代。 对于每次循环,它将
- 调用
SetQubitState
以便对第一个量子比特设置指定的 initial
值。
- 再次调用
SetQubitState
以将第二个量子比特设置为 Zero
状态。
- 使用
M
运算来测量每个量子比特。
- 存储每个量子比特的、返回
One
的测量数目。
- 循环完成后,它会再次调用
SetQubitState
以将量子比特重置为已知状态 (Zero
),使其他对象能够以已知状态分配量子比特。 这是 use
语句所必需的。
- 最后,它使用
Message
函数在返回结果之前将消息输出到控制台。
通过命令提示符运行代码
在继续执行叠加和纠缠过程之前,测试到目前为止的代码,以查看量子比特的初始化和测量。
若要将代码作为独立程序运行,当你运行 dotnet run
命令时,Q# 编译器需要知道在何处启动程序。 这是通过在 Q# 文件中你要运行的运算前面直接添加 @EntryPoint()
来完成的:在本例中为 TestBellState
运算。
只有独立的 Q# 程序才需要 @EntryPoint()
。 在 Jupyter Notebook 中运行 Q# 程序或者从 Python 或 .NET 宿主文件调用 Q# 程序时,不需要此参数,如果包含它,会引发错误。
program.qs
文件现在应如下所示:
namespace Bell {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
@EntryPoint()
operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' we saw:
if resultQ1 == One {
set numOnesQ1 += 1;
if resultQ2 == One {
set numOnesQ2 += 1;
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Return times we saw |0>, times we saw |1>
Message("q1:Zero, One q2:Zero, One");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
若要运行该程序,需要从命令提示符指定 count
和 initial
参数。 例如,--count 1000
和 --initial One
将第一个量子比特初始化为 One
并测量每个量子比特 1000 次。 运行以下命令:
dotnet run --count 1000 --initial One
你应会看到以下输出:
Q1:Zero/One Q2:Zero/One
(0, 1000, 1000, 0)
由于尚未操作量子比特,因此它们保留了初始值:第一个量子比特每次返回 One
,第二个量子比特返回 Zero
。
如果使用 --initial Zero
运行该程序,则应会注意到,第一个量子比特每次也返回 Zero
。
dotnet run --count 1000 --initial Zero
Q1:Zero/One Q2:Zero/One
(1000, 0, 1000, 0)
将量子比特置于叠加状态
目前,程序中的量子比特都处于经典状态,即,要么为 1,要么为 0。 之所以知道这一点,是因为该程序会将量子比特初始化为已知状态,并且你未添加任何过程来操作量子比特。 在纠缠量子比特之前,需要将第一个量子比特置于叠加状态,在此状态下,量子比特的测量会在 50% 的时间返回 Zero
,在 50% 的时间返回 One
。 从概念上讲,可将量子比特视为介于 Zero
和 One
之间的一种中间状态。
Q# 提供了 H
(Hadamard) 运算用于将量子比特置于叠加状态。 回顾前面的使用测量初始化量子比特过程中的 X
运算,它将量子比特从 0 翻转为 1(或反之);H
运算将量子比特中途翻转为 0 或 1 相等概率状态。 测量时,叠加的量子比特应返回大致相等数量的 Zero
和 One
结果。
修改 TestBellState
运算中的代码以包含 H
运算:
for test in 1..count {
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1); // Add the H operation after initialization and before measurement
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
现在,当你运行该程序时,可以看到第一个量子比特的叠加结果:
dotnet run --count 1000 --initial One
Q1:Zero/One Q2:Zero/One
(523, 477, 1000, 0) // results will vary
每次运行该程序,第一个量子比特的结果略有不同,但在大约 50% 的时间为 One
,大约 50% 的时间为 Zero
,而第二个量子比特的结果始终保持为 Zero
。
dotnet run --count 1000 --initial One
Q1:Zero/One Q2:Zero/One
(510, 490, 1000, 0)
将第一个量子比特初始化为 Zero
会返回类似的结果。
dotnet run --count 1000 --initial Zero
Q1:Zero/One Q2:Zero/One
(504, 496, 1000, 0)
纠缠两个量子比特
如前所述,纠缠的量子比特的连接方式使得它们不能相互独立地描述。 也就是说,无论一个量子比特发生了什么运算,纠缠的量子比特也会发生这种运算。 这样,只需测量一个量子比特的状态,就能知道另一个量子比特的最终状态,而无需测量它。 (此示例使用两个量子比特;但是,也可以纠缠三个或更多量子比特)。
Q# 提供了 CNOT
(Controlled-NOT 的缩写)运算用于实现纠缠。 对两个量子比特运行此操作的结果是,在第一个量子比特是 One
的情况下翻转第二个量子比特。
在程序中紧接在 H
运算的后面添加 CNOT
运算。 完整程序应如下所示:
namespace Bell {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
@EntryPoint()
operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1);
CNOT(q1, q2); // Add the CNOT operation after the H operation
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' we saw:
if resultQ1 == One {
set numOnesQ1 += 1;
if resultQ2 == One {
set numOnesQ2 += 1;
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Return times we saw |0>, times we saw |1>
Message("q1:Zero, One q2:Zero, One");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
现在,当你运行该程序时:
dotnet run --count 1000 --initial One
Q1:Zero/One Q2:Zero/One
(502, 498, 502, 498)
第一个量子比特的统计信息没有变化(测量后返回 Zero
或 One
的概率各为50%),但第二个量子比特的测量结果始终与第一个量子比特的测量结果相同。 CNOT
运算已将两个量子比特纠缠在一起,因此,无论其中一个量子比特发生了什么运算,另一个量子比特也会发生这种运算。
继续探索其他量子算法和技术:
- 教程实现 Grover 的搜索算法介绍了如何编写一个使用 Grover 搜索算法解决图形颜色问题的 Q# 程序。
- 教程在 Q# 中编写和模拟量子比特级程序介绍了如何编写一个直接寻址特定量子比特的 Q# 程序。
- Quantum Katas 是基于 Jupyter Notebook 的自定进度的教程和编程练习,旨在同时教授量子计算的元素和 Q# 编程。