你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
在本教程中,你将编写一个 Q# 程序来操作和度量量子比特,并演示叠加和纠缠的影响。 你准备两个处于特定量子状态的量子比特,了解如何对量子比特 Q# 进行操作以更改其状态,并演示叠加和纠缠的影响。 您逐步构建Q#程序,以引入量子比特状态、操作和测量。
下面是在开始之前要了解的一些关键概念:
- 经典位保存单个二进制值(如 0 或 1),而量子比特的状态则可以是 0 和 1 的双量子状态的叠加。 每种可能的量子状态具有一个关联的概率幅度。
- 测量量子比特的行为会产生具有特定概率的二进制结果,并将量子比特从叠加态中改变状态。
- 可以纠缠多个量子比特,以便无法相互独立地描述它们。 也就是说,无论纠缠对中的一个量子比特发生什么情况,另一个量子比特也会发生相同的情况。
本教程介绍以下操作:
- 创建 Q# 操作以将量子比特初始化为所需状态。
- 将量子比特置于叠加状态。
- 纠缠一对量子比特。
- 测量量子比特并观察结果。
提示
若要加速量子计算之旅,请查看 Azure Quantum 代码,这是 Azure Quantum 网站的独特功能。 在这里,你可以运行内置Q#示例或你自己的Q#程序,通过提示生成新Q#代码,在 VS Code for the Web 中打开并运行代码,只需单击一下,并询问 Copilot 有关量子计算的任何问题。
先决条件
若要在 Copilot for Azure Quantum 中运行代码示例,需要:
- Microsoft(MSA)电子邮件帐户。
有关 Copilot 的详细信息,请参阅 探索 Azure Quantum。
将量子位初始化为已知状态
第一步是定义一个 Q# 运算,用于将量子比特初始化为已知状态。 可以调用此操作将量子比特设置为经典状态,这意味着在测量时,它将返回 Zero 100% 的时间或返回 One 100% 的时间。 测量量子比特将返回一个 Q# 类型,该类型 Result只能有一个值 Zero 或 One。
打开适用于 Azure Quantum 的 Copilot 并将以下代码复制到代码编辑器窗口中。 先不要选择“运行”;你将在本教程的稍后部分运行代码。
import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
该代码示例引入了用于变换量子比特状态的两个标准运算(M 和 X)。
SetQubitState 运算:
- 采用两个参数:一个类型
Result,命名为desired,表示量子位应该处于Zero或One的状态,以及一个类型Qubit。 - 执行测量运算
M,以测量量子比特的状态(Zero或One),并将结果与desired中指定的值进行比较。 - 如果度量值与比较的值不一致,它会运行
X运算,将量子比特的状态翻转到度量结果为Zero和One的概率相反的位置。 这样,SetQubitState会始终将目标量子比特置于所需状态下。
编写测试操作以测试 Bell 状态
接下来,为了演示 SetQubitState 运算的效果,请创建名为 Main 的另一个运算。 此操作分配两个量子比特,调用 SetQubitState 将第一个量子比特设置为已知状态,然后测量量子比特以查看结果。
将以下代码复制到代码编辑器窗口中,在SetQubitState操作下方。
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// 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' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
在代码中,变量countinitial分别设置为1000和One分别。 这会将第一个量子比特初始化为 One,并测量每个量子比特 1000 次。
Main 运算:
- 设置计数器的变量和初始量子比特状态。
- 调用
use语句以初始化两个量子比特。 - 循环
count迭代。 对于每个循环,它:- 调用
SetQubitState以便对第一个量子比特设置指定的initial值。 - 再次调用
SetQubitState以将第二个量子比特设置为Zero状态。 - 使用
M运算来测量每个量子比特。 - 存储每个量子比特的、返回
One的测量数目。
- 调用
- 循环完成后,它会再次调用
SetQubitState以将量子比特重置为已知状态 (Zero),使其他对象能够以已知状态分配量子比特。use语句要求进行重置。 - 最后,它使用
Message函数将结果打印到 Copilot 输出窗口,然后再返回结果。
在 Copilot for Azure Quantum 中运行代码
在继续执行叠加和纠缠的过程之前,可以测试到此点的代码,以查看量子比特的初始化和度量。
若要将代码作为独立程序运行, Q# Copilot 中的编译器需要知道 从何处 启动程序。 由于未指定命名空间,编译器会将默认入口点识别为 Main 操作。 有关详细信息,请参阅 Projects 和隐式命名空间。
此时,程序 Q# 应如下所示:
import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// 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' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
将完整的代码示例复制并粘贴到 Azure Quantum 代码窗口的
Q1 - Zeros: 0
Q1 - Ones: 1000
Q2 - Zeros: 1000
Q2 - Ones: 0
由于尚未操作这些量子比特,因此它们保留了自己的初始值:第一个量子比特每次都返回 One,第二个量子比特返回 Zero。
如果将值 initial 更改为 Zero 并再次运行程序,则应观察第一个量子比特每次也会返回 Zero 。
Q1 - Zeros: 1000
Q1 - Ones: 0
Q2 - Zeros: 1000
Q2 - Ones: 0
提示
选择 Ctrl-Z 或 “编辑 > 撤消 ”,并在对代码引入测试更改之前保存文件,然后再再次运行它。
将量子比特置于叠加状态
目前,程序中的量子比特都处于 经典状态,即它们要么为 1 或 0。 之所以知道这一点,是因为该程序会将量子比特初始化为已知状态,并且你未添加任何过程来操作量子比特。 在纠缠量子比特之前,将第一个 量子比特置于叠加状态,其中量子比特的度量返回 Zero 大约 50% 的时间和 One 大约 50% 的时间。 从概念上讲,量子比特可以视为测量概率相等 Zero 或 One。
为了使量子比特处于叠加状态,Q# 提供了 H(即 Hadamard)运算。 回顾一下前面X过程中的 运算,该运算将量子比特从 0 翻转到 1(反之亦然);H 运算会将量子比特在中间位置翻转到 Zero 或 One 的相等概率状态。 测量时,叠加的量子比特应返回大致相等数量的 Zero 和 One 结果。
在Main操作中,通过重置初始值为One并插入一行针对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);
...
现在,运行程序时,可以看到处于叠加态的第一个量子位的结果。
Q1 - Zeros: 523 // results vary
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0
每次运行程序时,第一个量子比特的结果会略有不同,但接近 50% One 和 50% Zero,而第二个量子比特的结果始终保持 Zero。
Q1 - Zeros: 510
Q1 - Ones: 490
Q2 - Zeros: 1000
Q2 - Ones: 0
将第一个量子比特初始化为 Zero 会返回类似的结果。
Q1 - Zeros: 504
Q1 - Ones: 496
Q2 - Zeros: 1000
Q2 - Ones: 0
注意
通过在 Copilot for Azure Quantum 中移动滑块并增加拍摄次数,可看到叠加结果在拍摄分布上稍有变化。
纠缠两个量子比特
如前所述,纠缠的量子比特的连接方式使得它们不能相互独立地描述。 也就是说,无论一个量子比特发生了什么运算,纠缠的量子比特也会发生这种运算。 这样,只需测量一个量子比特的状态,就能知道另一个量子比特的最终状态,而无需测量它。 (此示例使用两个量子比特;但是,也可以纠缠三个或更多量子比特)。
为实现纠缠,Q# 提供了 CNOT 运算(表示 Controlled-NOT)。 对两个量子比特运行此操作的结果是,在第一个量子比特是 One 的情况下翻转第二个量子比特。
在程序中紧接在 CNOT 运算的后面添加 H 运算。 完整程序应如下所示:
import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = Zero;
// 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' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
现在,运行程序时,应会看到如下所示的内容:
Q1 - Zeros: 502 // results will vary
Q1 - Ones: 498
Q2 - Zeros: 502
Q2 - Ones: 498
请注意,第一个量子比特的统计信息尚未更改(测量后仍有大约 50/50 个概率ZeroOne),但第二个量子比特的度量结果始终与第一个量子位的测量相同,无论运行程序多少次。
CNOT 运算将这两个量子比特纠缠在一起,因此,不论其中一个发生什么情况,另一个也会同样发生。
先决条件
若要在本地开发环境中开发和运行代码示例,请执行以下操作:
- 最新版本的 Visual Studio Code 或打开网页版 VS Code。
- 最新版本的 Azure Quantum 开发工具包扩展。 有关安装详细信息,请参阅 设置 QDK 扩展。
创建新 Q# 文件
- 打开 Visual Studio Code 并选择“文件”>“新建文本文件”以创建新文件。
- 将文件另存为
CreateBellStates.qs。 此文件将包含 Q# 程序的代码。
将量子位初始化为已知状态
第一步是定义一个 Q# 运算,用于将量子比特初始化为已知状态。 可以调用此操作将量子比特设置为经典状态,这意味着它 Zero 返回 100% 的时间或返回 One 100% 的时间。
Zero 和 One 是表示对量子比特进行测量时仅有的两个可能结果的 Q# 值。
打开 CreateBellStates.qs 并复制以下代码:
import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
该代码示例引入了用于变换量子比特状态的两个标准运算(M 和 X)。
SetQubitState 运算:
- 采用两个参数:一个类型
Result,命名为desired,表示量子位应该处于Zero或One的状态,以及一个类型Qubit。 - 执行测量运算
M,以测量量子比特的状态(Zero或One),并将结果与desired中指定的值进行比较。 - 如果度量值与比较的值不一致,它会运行
X运算,将量子比特的状态翻转到度量结果为Zero和One的概率相反的位置。 这样,SetQubitState会始终将目标量子比特置于所需状态下。
编写测试操作以测试 Bell 状态
接下来,为了演示 SetQubitState 运算的效果,请创建名为 Main 的另一个运算。 此操作分配两个量子比特,调用 SetQubitState 将第一个量子比特设置为已知状态,然后测量量子比特以查看结果。
将以下运算添加到 CreateBellStates.qs 文件中的 SetQubitState 运算后面:
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// 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' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
在代码中,变量countinitial分别设置为1000和One分别。 此步骤将第一个量子比特初始化为One,并测量每个量子比特1000次。
Main 运算:
- 采用两个参数:
count,即测量的运行次数;initial,即要将量子比特初始化到的所需状态。 - 调用
use语句以初始化两个量子比特。 - 循环
count迭代。 对于每个循环,它:- 调用
SetQubitState以便对第一个量子比特设置指定的initial值。 - 再次调用
SetQubitState以将第二个量子比特设置为Zero状态。 - 使用
M运算来测量每个量子比特。 - 存储每个量子比特的、返回
One的测量数目。
- 调用
- 循环完成后,它会再次调用
SetQubitState以将量子比特重置为已知状态 (Zero),使其他对象能够以已知状态分配量子比特。use语句要求重置量子比特。 - 最后,它使用
Message函数在返回结果之前将消息输出到控制台。
运行代码
在继续执行叠加和纠缠过程之前,测试到目前为止的代码,以查看量子比特的初始化和测量。
若要将代码作为独立程序运行, Q# 编译器需要知道 从何处 启动程序。 由于未指定命名空间,编译器会将默认入口点识别为 Main 操作。 有关详细信息,请参阅 Projects 和隐式命名空间。
此时,文件
CreateBellStates.qs应如下所示:import Microsoft.Quantum.Intrinsic.*; import Microsoft.Quantum.Canon.*; operation SetQubitState(desired : Result, target : Qubit) : Unit { if desired != M(target) { X(target); } } operation Main() : (Int, Int, Int, Int) { mutable numOnesQ1 = 0; mutable numOnesQ2 = 0; let count = 1000; let initial = One; // 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' returned: if resultQ1 == One { numOnesQ1 += 1; } if resultQ2 == One { numOnesQ2 += 1; } } // reset the qubits SetQubitState(Zero, q1); SetQubitState(Zero, q2); // Display the times that |0> is returned, and times that |1> is returned Message($"Q1 - Zeros: {count - numOnesQ1}"); Message($"Q1 - Ones: {numOnesQ1}"); Message($"Q2 - Zeros: {count - numOnesQ2}"); Message($"Q2 - Ones: {numOnesQ2}"); return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 ); }若要运行程序,请从菜单中选择“运行”,或按 Ctrl + F5。 程序在默认模拟器上运行
Main操作。输出将显示在调试控制台中。
Q1 - Zeros: 0 Q1 - Ones: 1000 Q2 - Zeros: 1000 Q2 - Ones: 0由于尚未操作这些量子比特,因此它们保留了自己的初始值:第一个量子比特每次都返回
One,第二个量子比特返回Zero。如果将值
initial更改为Zero并再次运行程序,则应观察第一个量子比特每次也会返回Zero。Q1 - Zeros: 1000 Q1 - Ones: 0 Q2 - Zeros: 1000 Q2 - Ones: 0
提示
选择 Ctrl-Z 或 “编辑 > 撤消 ”,并在对代码引入测试更改之前保存文件,然后再再次运行它。
将量子比特置于叠加状态
目前,程序中的量子比特都处于经典状态,即,要么为 1,要么为 0。 之所以知道这一点,是因为该程序会将量子比特初始化为已知状态,并且你未添加任何过程来操作量子比特。 在纠缠量子比特之前,将第一个 量子比特置于叠加状态,其中量子比特的度量返回 Zero 50% 的时间和 One 50% 的时间。 从概念上讲,可将量子比特视为介于 Zero 和 One 之间的一种中间状态。
为了使量子比特处于叠加状态,Q# 提供了 H(即 Hadamard)运算。 回顾一下前面X过程中的 运算,该运算将量子比特从 Zero 翻转到 One(反之亦然);H 运算会将量子比特在中间位置翻转到 Zero 或 One 的相等概率状态。 测量时,叠加的量子比特应返回大致相等数量的 Zero 和 One 结果。
修改
Main运算中的代码以包含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); ...现在,当你运行该程序时,可以看到第一个量子比特的叠加结果:
Q1 - Zeros: 523 // results will vary Q1 - Ones: 477 Q2 - Zeros: 1000 Q2 - Ones: 0每次运行程序时,第一个量子比特的结果会略有不同,但将接近 50%
One和 50%,Zero而第二个量子比特的结果始终保持不变Zero。Q1 - Zeros: 510 Q1 - Ones: 490 Q2 - Zeros: 1000 Q2 - Ones: 0将第一个量子比特初始化为
Zero会返回类似的结果。Q1 - Zeros: 504 Q1 - Ones: 496 Q2 - Zeros: 1000 Q2 - Ones: 0
纠缠两个量子比特
如前所述,纠缠的量子比特的连接方式使得它们不能相互独立地描述。 也就是说,无论一个量子比特发生了什么运算,纠缠的量子比特也会发生这种运算。 这样,只需测量一个量子比特的状态,就能知道另一个量子比特的最终状态,而无需测量它。 (此示例使用两个量子比特;但是,也可以纠缠三个或更多量子比特)。
为实现纠缠,Q# 提供了 CNOT 运算(表示 Controlled-NOT)。 对两个量子比特运行此操作的结果是,在第一个量子比特是 One 的情况下翻转第二个量子比特。
在程序中紧接在
CNOT运算的后面添加H运算。 完整程序应如下所示:import Microsoft.Quantum.Intrinsic.*; import Microsoft.Quantum.Canon.*; operation SetQubitState(desired : Result, target : Qubit) : Unit { if desired != M(target) { X(target); } } operation Main() : (Int, Int, Int, Int) { mutable numOnesQ1 = 0; mutable numOnesQ2 = 0; let count = 1000; let initial = Zero; // 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' returned: if resultQ1 == One { numOnesQ1 += 1; } if resultQ2 == One { numOnesQ2 += 1; } } // reset the qubits SetQubitState(Zero, q1); SetQubitState(Zero, q2); // Display the times that |0> is returned, and times that |1> is returned Message($"Q1 - Zeros: {count - numOnesQ1}"); Message($"Q1 - Ones: {numOnesQ1}"); Message($"Q2 - Zeros: {count - numOnesQ2}"); Message($"Q2 - Ones: {numOnesQ2}"); return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 ); }Q1 - Zeros: 502 Q1 - Ones: 498 // results will vary Q2 - Zeros: 502 Q2 - Ones: 498 Result: "(502, 498, 502, 498)"
第一个量子比特的统计信息没有变化(测量后返回 Zero 或 One 的概率各为50%),但第二个量子比特的测量结果始终与第一个量子比特的测量结果相同。 该 CNOT 操作纠缠了两个量子比特,因此,无论其中一个量子比特发生什么,都发生在另一个量子位上。
绘制频率直方图
让我们直观显示从运行量子程序多次获取的结果分布。 频率直方图有助于可视化这些结果的概率分布。
选择 视图 -> 命令面板,或按 Ctrl + Shift + P,然后输入 直方图,以显示 QDK: 运行文件并显示直方图 选项。 您还可以从命令列表中选择直方图,该列表位于
Main之前。 选择此选项可打开 Q# 直方图窗口。输入执行程序的 次 数,例如 100 次,然后按 Enter。 直方图显示在 Q# 直方图窗口中。
直方图中的每个条形图对应于可能的结果,其高度表示观察到结果的次数。 在这种情况下,有 50 个不同的唯一结果。 请注意,对于每个结果,第一个和第二个量子比特的度量结果始终相同。
提示
可以使用鼠标滚轮或触控板手势缩放直方图。 放大时,可以通过在滚动时按 Alt 平移图表。
选择一个栏以显示 该结果的百分比 。
选择左上角 的设置图标 以显示选项。 可以显示前 10 个结果、前 25 个结果或所有结果。 还可以将结果从高到低或从低到高对结果进行排序。
相关内容
浏览其他 Q# 教程:
- Grover 的搜索算法 演示如何编写 Q# 使用 Grover 搜索算法的程序。
- Quantum Fourier Transform 探索了如何编写 Q# 直接处理特定量子比特的程序。
- Quantum Katas 是自我节奏的教程和编程练习,旨在同时教授量子计算和Q#编程的元素。