你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
在本教程中,你将编写一个 Q# 程序,该程序将两个量子比特准备成特定的量子状态,对量子比特进行操作以使它们相互纠缠,并通过测量来展示叠加和纠缠的效果。 逐步构建你的Q#程序,引入量子比特状态、量子操作和测量。
在开始之前,请查看以下量子计算概念:
- 经典位保存单个二进制值(如 0 或 1),但量子比特可以叠加两种状态(0 和 1)。 每个可能的量子比特状态都由一组概率振幅描述。
- 测量量子比特的状态时,始终获得 0 或 1。 每个结果的概率取决于在进行度量时定义叠加状态的概率振幅。
- 可以纠缠多个量子比特,这样就不能彼此独立地描述它们。 在纠缠对中测量一个量子比特时,还可以获取有关另一个量子比特的信息,而无需测量它。
本教程中,您将学习如何:
- 创建 Q# 操作以将量子比特初始化为所需状态。
- 将量子比特置于叠加状态。
- 纠缠一对量子比特。
- 测量量子比特并观察结果。
提示
若要加速量子计算之旅,请查看 Azure Quantum 代码,这是 Microsoft Quantum 网站的独特功能。 在这里,你可以运行内置 Q# 示例或你自己的 Q# 程序,通过提示生成新 Q# 代码,在 VS Code for Web 中打开并运行代码,只需单击一下,并询问有关量子计算的 Copilot 问题。
先决条件
若要使用 Copilot for Azure Quantum 运行代码示例,必须具有Microsoft(MSA)电子邮件帐户。
有关适用于 Azure Quantum 的 Copilot 的详细信息,请参阅 探索 Azure Quantum。
将量子位初始化为已知状态
第一步是定义一个将Q#量子位初始化为所需经典比特状态(0 或 1)的操作。 该操作对一般量子态的量子比特进行测量,它返回Q#Result类型值,该值要么是Zero,要么是One。 如果度量结果与所需状态不同,则操作将翻转状态,以便操作每次返回所需状态。
打开 Copilot for Azure Quantum,清除默认代码,然后将以下代码复制到代码编辑器窗口中。 无法单独运行此代码,因为它尚不是完整的 Q# 程序。
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
该代码示例引入了两个标准Q#操作:M和X,用于转换量子比特的状态。
下面是关于SetQubitState操作工作原理的详细说明:
- 采用两个参数:一个
Result类型参数desired,它表示量子比特应达到的状态(Zero或One),以及一个Qubit类型参数。 - 执行度量运算,
M它测量量子比特(Zero或One)的状态,并将结果与你传递desired的值进行比较。 - 如果测量结果与值
desired不匹配,则对量子比特施加X操作。 此操作将翻转量子比特的状态,使得测量Zero和One的概率倒转。
编写测试操作以测试 Bell 状态
要在Q#程序中调用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 操作执行以下操作:
- 设置镜头数(
count)和初始量子比特状态(One)的变量。 - 调用
use语句以初始化两个量子比特。 - 循环实验
count次。 - 在循环中,调用
SetQubitState以设置第一个量子位上的指定initial值,然后再次调用SetQubitState以将第二个量子位设置为Zero状态。 - 在循环中应用
M操作来测量每个量子比特,然后存储返回One的每个量子比特的测量次数。 - 循环完成后,再次调用
SetQubitState以将量子比特重置为已知状态(Zero)。 必须重置使用语句分配的use量子位。 - 调用函数
Message以在输出窗口中打印结果。
在 Copilot for Azure Quantum 中运行代码
在编写叠加和纠缠代码之前,请测试当前程序以查看量子比特的初始化和度量。
若要将代码作为独立程序运行, Q# Copilot 中的编译器需要知道从何处启动程序。 由于未指定命名空间,编译器会将默认入口点识别为 Main 作。 有关详细信息,请参阅 Projects 和隐式命名空间。
程序 Q# 现在如下所示:
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 );
}
将完整的代码示例复制并粘贴到 Copilot for Azure Quantum 代码窗口中,将拍摄次数的滑块设置为“1”,然后选择“ 运行”。 结果显示在直方图和 结果 字段中。
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
将量子位放入叠加状态
目前,程序中的量子比特处于经典状态(1 或 0)就像常规计算机上的位一样。 若要纠缠量子比特,必须先将其中一个量子位置于相等叠加状态。 在相等叠加状态下测量量子比特时,返回 Zero 和 返回 One 的几率均为 50%。
若要将量子比特置于叠加状态,请使用 Hadamard 运算Q#H。 该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);
...
再次运行程序。 由于第一个量子位在测量时处于相等的叠加态,因此 Zero 和 One 的结果接近 50/50。 例如,输出如下所示:
Q1 - Zeros: 523
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0
每次运行程序时,第一个量子比特的结果会略有不同,但接近 50% One 和 50% Zero,而第二个量子比特的结果仍始终存在 Zero。
将第一个量子比特初始化为 Zero 而不是 One 再次运行程序。 你获得类似的结果,因为H操作将纯Zero状态和纯One状态转换为相等的叠加态。
注意
若要查看叠加结果在拍摄分布上的变化,请移动适用于 Azure Quantum 的 Copilot 中的滑块并增加拍摄次数。
纠缠两个量子比特
纠缠量子比特相互关联,无法相互独立描述。 测量一个纠缠量子比特的状态时,还知道另一个量子比特的状态而不测量它。 本教程使用包含两个纠缠量子比特的示例,但也可以纠缠三个或多个量子比特。
若要创建纠缠状态,请使用 Q#CNOT 或“Controlled-NOT”操作。 在将 CNOT 应用于两个量子比特时,一个是控制量子比特,另一个是目标量子比特。 如果控制量子比特的状态为 One,则 CNOT 操作将翻转目标量子比特的状态。 否则,CNOT 对量子位不执行任何作用。
在程序中紧接在 CNOT 运算的后面添加 H 运算。 完整的程序如下所示:
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
Q2 - Zeros: 502
Q2 - Ones: 498
第一个量子比特的统计信息仍然显示大约 50% 的几率测量 One 和 Zero,但第二个量子比特的测量结果现在并不总是 Zero。 每个量子比特的Zero结果数与One结果数相同。 第二个量子比特的度量结果始终与第一个量子比特的结果相同,因为两个量子比特纠缠在一起。 如果第一个量子比特被测量为Zero,则纠缠的量子比特也必须是Zero。 如果第一个量子位被测量为 One ,那么纠缠量子位也必须是 One。
先决条件
若要在本地开发环境中开发和运行代码示例,请安装以下工具:
- 最新版本的 Visual Studio Code(VS Code) 或打开 网页版 VS Code。
- Azure Quantum 开发工具包 (QDK) 扩展的最新版本。 有关安装详细信息,请参阅 设置 QDK 扩展。
创建新 Q# 文件
- 在 VS Code 中,打开 “文件 ”菜单,然后选择“ 新建文本文件 ”以创建新文件。
- 将文件另存为
CreateBellStates.qs。 此文件是编写程序代码的位置 Q# 。
将量子位初始化为已知状态
第一步是定义一个Q# 操作,将量子位初始化为所需的经典状态(0 或 1)。 该操作测量处于普遍量子态的量子比特,并返回Zero或One类型的Q#Result值。 如果度量结果与所需状态不同,则操作将翻转状态,以使操作在 100% 的情况下返回所需状态。
打开 CreateBellStates.qs 并复制以下代码:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
该代码示例引入了两个标准 Q# 操作,M 和 X,用于转换量子比特的状态。
以下是SetQubitState操作工作原理的详细说明:
- 采用两个参数:一个名为
desired的Result类型参数,表示量子比特的期望状态(Zero或One),以及一个Qubit类型参数。 - 执行度量运算,
M它测量量子比特(Zero或One)的状态,并将结果与你传递desired的值进行比较。 - 如果度量结果与
desired的值不匹配,则对量子比特应用X操作。 此操作会翻转量子比特的状态,以便Zero和One的测量概率互换。
编写测试操作以测试 Bell 状态
若要调用Q#程序中的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)和初始量子比特状态(One)的变量。 - 调用
use语句以初始化两个量子比特。 - 循环执行试验
count次。 - 在循环中,调用
SetQubitState以设置第一个量子位上的指定initial值,然后再次调用SetQubitState以将第二个量子位设置为Zero状态。 - 在循环中,应用
M操作来测量每个量子比特,然后存储每个量子比特返回One的测量次数。 - 循环完成后,再次调用
SetQubitState以将量子比特重置为已知状态(Zero)。 您必须重置使用use语句分配的量子位。 - 调用函数
Message以在控制台中打印结果。
运行代码
在编写叠加和纠缠代码之前,请测试当前程序以查看量子比特的初始化和度量。
若要将代码作为独立程序运行, Q# 编译器需要知道从何处启动程序。 由于未指定命名空间,编译器会将默认入口点识别为 Main 作。 有关详细信息,请参阅 Projects 和隐式命名空间。
文件 CreateBellStates.qs 现在如下所示:
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 );
}
若要运行程序,请从代码镜头中选择Main命令,或输入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
将量子位放入叠加状态
目前,程序中的量子比特处于经典状态(1 或 0)就像常规计算机上的位一样。 若要纠缠量子比特,必须先将其中一个量子位置于相等叠加状态。 在测量处于相等叠加态的量子比特时,有 50% 的机会返回 Zero,以及 50% 的机会返回 One。
若要将量子位(qubit)置于叠加态,请使用Q#H,即Hadamard运算。 该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);
...
再次运行程序。 因为第一个量子位在测量时处于等概率叠加态,你得到的Zero和One结果接近于50/50。 例如,输出如下所示:
Q1 - Zeros: 523
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0
每次运行程序时,第一个量子比特的结果会略有不同,但接近 50% One 和 50% Zero,而第二个量子比特的结果仍始终存在 Zero。
将第一个量子比特初始化为 Zero 而不是 One 再次运行程序。 你获得类似的结果,因为操作H 将纯Zero 状态和纯One 状态转换为相等的叠加状态。
纠缠两个量子比特
纠缠量子比特相互关联,无法相互独立描述。 测量一个纠缠量子比特的状态时,还知道另一个量子比特的状态而不测量它。 本教程使用包含两个纠缠量子比特的示例,但也可以纠缠三个或多个量子比特。
若要创建纠缠状态,请使用 Q#CNOT,或 Controlled-NOT 作。 当您将 CNOT 应用于两个量子比特时,一个量子比特是控制量子比特,另一个是目标量子比特。 如果控制量子比特的状态为 One,那么 CNOT 操作将翻转目标量子比特的状态。 否则,CNOT 对量子位不执行任何操作。
在程序中紧接在 CNOT 运算的后面添加 H 运算。 完整的程序如下所示:
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
Q2 - Zeros: 502
Q2 - Ones: 498
第一个量子比特的统计信息仍然显示,测量One和Zero的机会各约为50%,但现在第二个量子比特的测量结果并不总是Zero。 每个量子比特的Zero结果数和One结果数相同。 第二个量子比特的度量结果始终与第一个量子比特的结果相同,因为两个量子比特纠缠在一起。 如果测量第一个量子位为 Zero,那么纠缠量子比特也必须为 Zero。 如果第一个量子比特被测量为 One,则纠缠量子比特也必须是 One。
绘制频率直方图
若要绘制显示多次运行程序时结果分布的频率直方图,请完成以下步骤:
在 VS Code 中打开文件
CreateBellStates.qs。打开 “视图 ”菜单,然后选择 “命令面板”。
输入 直方图 以显示 QDK:运行文件并显示直方图 选项。 或者,从位于操作之前的CodeLens选项中选择
Main命令。 然后,输入一些镜头(例如 100)。 直方图将在 Q# 新选项卡中打开。直方图中的每个条形图对应于纠缠线路运行 1000 次时可能的结果。 条形图的高度表示结果发生的次数。 例如,下面的直方图显示具有 50 个唯一结果的分布。 请注意,对于每个结果,第一个和第二个量子比特的度量结果始终相同。
提示
若要放大直方图,请使用鼠标滚轮或触控板手势。 若要在放大时平移图表,请按住 Alt ,同时滚动。
选择一个条形图以显示总拍摄数中达到该结果的比例。
选择左上角的设置 图标 以显示可视化效果选项。
再次运行代码,但这次运行 1000 次实验。 随着拍摄次数的增加,结果分布接近正态分布。
相关内容
浏览其他 Q# 教程:
- Grover 的搜索算法 演示如何编写 Q# 使用 Grover 搜索算法的程序。
- Quantum Fourier Transform 探索了如何编写 Q# 直接处理特定量子比特的程序。
- Quantum Katas 是自定进度的教程和编程练习,同时教授量子计算和Q#编程的元素。