量子计算实践指南-全-

量子计算实践指南(全)

原文:zh.annas-archive.org/md5/d36b115ba52130eb6fd1400da3fcf417

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

IBM Quantum Experience®Qiskit® 一起构成了一个流行且易于使用的量子计算平台。它们让您能够访问和编程实际的 IBM 量子计算机硬件,但您也可以在本地和基于云的模拟器上运行您的代码。

本书旨在教授您如何在 Python® 环境中实现量子编程,首先从基础水平开始,然后过渡到更高级的示例。本地可安装的 量子信息科学工具包Qiskit)软件建立在 Python 之上,代表了今天学习量子计算最易于访问的工具。

在本书的所有食谱中,我们将逐步介绍 Qiskit 类和方法,从最基本的概念开始,例如安装和升级 Qiskit,检查您正在运行的版本,等等。然后,我们将继续了解创建和运行量子程序所需的构建块,以及如何将 Qiskit 组件集成到您自己的混合量子/经典程序中,以利用 Python 强大的编程功能。

我们将探索、比较和对比 有噪声的中规模量子计算机NISQ)和通用容错量子计算机,使用模拟器和实际硬件进行,仔细研究模拟噪声后端以及如何在实际硬件上减轻噪声和错误,实现单比特量子纠错中的 Shor 代码方法。

最后,我们将探讨量子算法,看看它们与经典算法有何不同。我们将更深入地研究编码 Grover 算法,然后使用 Qiskit Aqua 运行 Grover 和 Shor 算法的版本,以展示您如何直接在 Qiskit 代码中重用已构建的算法。我们以 Qiskit、IBM 的量子信息科学工具包及其组成部分(Terra、Aer、Ignis 和 Aqua)的全面巡视为此。

我们还将使用在线 IBM Quantum Experience® 拖放式量子计算用户界面。本书中我们所做的一切,以及更多,都可以在 IBM Quantum Experience® 的云平台上进行编码。

每一章都包含代码示例,以解释每个食谱中教授的原则。

本书面向对象

本书面向开发者、数据科学家、研究人员和希望了解如何使用 Qiskit® 和 IBM Quantum Experience® 来实现量子解决方案的量子计算爱好者。对量子计算和 Python 编程语言的基本知识有益。

本书涵盖内容

这本烹饪书采用问题-解决方案-探索的方法,通过 IBM Quantum Experience®、Qiskit® 和 Python 的帮助,理解编程量子计算机的细微差别。

第一章, 准备您的环境,将指导您如何在本地工作站上安装 Qiskit®作为 Python 3.5 扩展。您还将注册 IBM Quantum Experience®,获取您的 API 密钥,并获取示例代码。

第二章, 使用 Python 进行量子计算和量子比特,展示了如何使用 Python 编写简单的脚本,以介绍比特和量子比特的概念以及量子门的工作原理,而无需使用 Qiskit®或 IBM Quantum Experience®。

第三章, IBM Quantum Experience® – 量子拖放,探讨了 IBM Quantum Experience®,这是 IBM Quantum 的在线、基于云的拖放工具,用于编程量子计算机。在这里,您将编写一个简单的程序,并学习如何在 Qiskit®和 IBM Quantum Experience®之间进行切换。

第四章, 从 Terra 开始起步,探索了一组基本的量子程序或电路,以检查概率计算、叠加、纠缠等基本概念。我们还将在我们实际的物理 IBM 量子计算机上运行我们的第一个程序。

第五章, 使用 Qiskit®游览 IBM Quantum®硬件,探讨了 IBM Quantum®后端,并探索了影响您量子程序结果的各种物理方面。

第六章, 理解 Qiskit®门库,概述了 Qiskit®提供的开箱即用的量子门,以了解它们对您的量子比特做了什么。我们还将查看构成所有其他量子门的基础通用量子门,并从单量子比特门扩展到双、三量子比特以及更多量子比特门,这些是更高级量子电路所需的。

第七章, 使用 Aer 模拟量子计算机,帮助您在您可以使用本地或云端的模拟器集合上运行您的电路。您甚至可以将您的模拟器设置为模仿 IBM Quantum®后端的行为,以便在您的本地机器上以实际条件测试您的电路。

第八章, 使用 Ignis 清理您的量子行为,解释了如何通过理解我们的量子比特的行为来清理测量结果,并探讨了如何通过使用噪声缓解电路(如 Shor 码)来纠正噪声。

第九章, Grover 搜索算法,构建了 Grover 搜索算法,这是一种经典搜索算法的平方加速。我们将使用独特的量子工具,量子相位回弹。我们将构建几个不同版本的算法,以便在模拟器和 IBM Quantum®后端上运行。

第十章通过 Aqua 了解算法,使用了两个最著名的量子算法的预制的 Qiskit Aqua 版本:Grover 搜索算法和 Shor 分解算法。我们还快速浏览了 Qiskit Aqua 算法库。

为了充分利用本书

如果您对基本量子计算概念有所了解,这将有所帮助;我们不会在证明或更深入的细节上花费太多时间。Python 编程技能也有帮助,尤其是在我们开始构建稍微复杂一些的混合量子/经典程序时。对线性代数(包括向量和矩阵乘法)的基本理解将肯定有助于您理解量子门的工作原理,但我们让 Python 和 NumPy 做艰苦的工作。

Qiskit® 支持 Python 3.6 或更高版本。本书中的代码示例是在 Anaconda 1.9.12 (Python 3.7.0) 上测试的,使用了其捆绑的 Spyder 编辑器以及 Qiskit® 0.21.0,并在 IBM Quantum Experience® Code lab 网上环境进行了测试。我们建议读者使用相同的配置。

图片

如果您使用的是本书的电子版,我们建议您亲自输入代码或通过 GitHub 仓库(下一节中提供链接)访问代码。这样做将帮助您避免与代码复制粘贴相关的任何潜在错误。

下载示例代码文件

您可以从 GitHub 下载本书的示例代码文件:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience。如果代码有更新,它将在现有的 GitHub 仓库中更新。

我们还有其他来自我们丰富的图书和视频目录的代码包,可在 github.com/PacktPublishing/ 获取。查看它们!

下载彩色图像

我们还提供了一份包含本书中使用的截图/图表的彩色图像的 PDF 文件。您可以从这里下载:static.packt-cdn.com/downloads/9781838828448_ColorImages.pdf

使用的约定

本书使用了多种文本约定。

文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。以下是一个示例:“log_length(oracle_input,oracle_method) 函数接受作为输入的或然输入(日志或二进制)和或然方法(逻辑表达式或位串),并返回 Grover 电路需要包含的理想迭代次数。”

代码块应如下设置:

def log_length(oracle_input,oracle_method):
    from math import sqrt, pow, pi, log
    if oracle_method=="log":
        filtered = [c.lower() for c in oracle_input if             c.isalpha()]
        result = len(filtered)

任何命令行输入或输出都应如下编写:

$ conda create -n environment_name python=3

粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“点击作业结果框将打开结果页面,并显示您刚刚运行的作业的最终结果。”

小贴士或重要注意事项

显示如下。

章节列表

在这本书中,您将找到几个频繁出现的标题(准备工作如何操作……工作原理……还有更多……参考内容)。

为了清楚地说明如何完成食谱,请按照以下方式使用这些部分:

准备工作

本节将向您介绍在食谱中可以期待的内容,并描述如何设置任何软件或任何为食谱所需的初步设置。

如何操作……

本节包含遵循食谱所需的步骤。

工作原理……

本节通常包含对上一节发生情况的详细解释。

还有更多……

本节包含有关食谱的附加信息,以便您对食谱有更深入的了解。

参考内容

本节提供了对其他有用信息的链接,这些信息对食谱很有帮助。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请在邮件主题中提及书名,并通过 customercare@packtpub.com 给我们发送邮件。

勘误表:尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,如果您能向我们报告,我们将不胜感激。请访问www.packtpub.com/support/errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。

盗版:如果您在互联网上以任何形式发现我们作品的非法副本,如果您能向我们提供位置地址或网站名称,我们将不胜感激。请通过 copyright@packtpub.com 与我们联系,并提供材料的链接。

如果您有兴趣成为作者:如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

评论

请留下评论。一旦您阅读并使用过这本书,为什么不在您购买它的网站上留下评论呢?潜在读者可以查看并使用您的客观意见来做出购买决定,我们 Packt 可以了解您对我们产品的看法,我们的作者也可以看到他们对书籍的反馈。谢谢!

有关 Packt 的更多信息,请访问packt.com

第一章:第一章: 准备您的环境

在您开始编写量子程序之前,您必须有一个 Python 环境来执行您的代码。本书中的示例可以在使用 IBM Quantum®提供的 Qiskit®开发者环境在您的本地机器上运行,也可以在 IBM Quantum Experience®的在线环境中运行。

在本章中,我们将探讨这两种环境,帮助您在 IBM Quantum Experience®上获得登录账户,并安装本地版本的Qiskit®。我们还将讨论快速发展的开源 Qiskit®环境,以及如何保持您的本地环境更新。

我们将涵盖以下食谱:

  • 创建您的 IBM Quantum Experience®账户

  • 安装 Qiskit®

  • 下载代码示例

  • 安装您的 API 密钥并访问您的提供商

  • 保持您的 Qiskit®环境更新

因此,让我们开始吧。本章及其内容非常重要,因为它为您提供了构建 Qiskit®未来的基础。请花一点时间设置好,然后开始使用本书中的食谱进行量子编程,以在 Qiskit®上入门。为了快速入门,您还可以获取并运行本书提供的示例食谱代码。

技术要求

我们将在本章中讨论的食谱可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter01

您可以在本章中设置的本地 Qiskit®环境中运行本书中的食谱。您还可以在本书中的c1_r1_version.py食谱中运行大多数食谱,该食谱列出了您运行食谱的环境中的 Qiskit®安装版本。

有关下载食谱的信息,请参阅下载代码示例

您选择安装 Qiskit®的本地环境必须已安装Python 3.5 或更高版本(截至本书编写时)。有关 Qiskit®安装的最新要求的详细信息,请参阅qiskit.org/documentation/install.html上的 Qiskit®要求页面。

IBM Quantum®建议使用 Python 的 Anaconda 发行版(www.anaconda.com/distribution/),并使用虚拟环境来保持您的 Qiskit®安装与您的常规 Python 环境隔离。

对虚拟环境不熟悉?

虚拟环境提供了隔离的 Python 环境,您可以分别修改它们。例如,您可以为您安装的 Qiskit®创建一个隔离的环境。然后,您将仅在那种环境中安装 Qiskit®,而不会触及基础环境中的 Python 框架,这将包含一个未受损害的 Python 版本。

随着 Qiskit®发布新版本的包,实际上没有任何阻止您为 Qiskit®的每个更新版本创建一个新的隔离环境,以保留您用于 Qiskit®量子编程的旧版和稳定版本,以及一个可以测试 Qiskit®更新版本的新环境。您将在保持 Qiskit®环境更新菜谱中了解更多信息。

创建您的 IBM Quantum Experience®账户

您探索 IBM 量子计算机编程的钥匙是您的IBM Quantum Experience®账户。这个免费账户为您提供了访问在线 IBM Quantum Experience®界面和那里可用的编程工具。实际上,您不需要 IBM Quantum Experience®账户来测试 IBM Quantum Experience®或安装 Qiskit®,但您需要运行程序在免费可用的 IBM 量子计算机上,毕竟这也是您最初阅读这本书的原因。

准备工作

要设置您的 IBM Quantum Experience®账户,您可以使用 IBMid 登录,或使用以下之一:

  • Google 账户

  • GitHub 账户

  • LinkedIn 账户

  • Twitter 账户

  • 电子邮件地址

如何操作...

  1. 在您的浏览器(Google Chrome 似乎效果最好)中,访问此链接:quantum-computing.ibm.com/login

  2. 输入您的 IBMid 凭证或选择其他登录方法。

    您也可以跳过登录,这将使您能够访问 IBM Quantum Experience®,但您的量子电路将限制为 3 个量子位,并且只能使用模拟后端。

  3. 一旦您登录,现在您拥有了一个激活的 IBM Quantum Experience®账户,您将发现自己位于主仪表板:Figure 1.1 – The IBM Quantum Experience® home page

    Figure 1.1 – The IBM Quantum Experience® home page

  4. 从这里,您有几个路径:

    前往作曲家开始使用图形用户界面构建您的量子程序。点击左侧菜单的电路作曲家图标 (),然后转到第三章IBM Quantum Experience® – 量子拖放

    如果您想在安装本地 Qiskit®实例之前开始用 Python 编写量子程序,您可以去 Qiskit®笔记本,在 Jupyter Notebook Python 环境中开始编写您的量子程序。点击量子实验室左侧菜单图标 (A picture containing drawing    自动生成的描述),点击新建笔记本,然后转到第四章从 Terra 开始入门

    如果您想继续使用本书所写的 Qiskit®路径,现在您可以退出 IBM Quantum Experience®,并在本地机器上安装 Qiskit®。

相关链接

安装 Qiskit®

在你的 Python 环境准备就绪并可以运行,以及你的 IBM Quantum Experience® 账户设置完成后,你现在可以使用 pip 来安装 Qiskit® Python 扩展。这个过程大约需要 10 分钟左右,之后你就可以使用你的 Python 命令行,或者你喜欢的 Anaconda Python 解释器来开始编写你的量子程序。

准备工作

这个配方提供了关于安装 Qiskit® 的通用方法的信息,并没有涉及操作系统差异或一般安装故障排除的细节。

有关 Qiskit® 安装最新要求的详细信息,请参阅 Qiskit® 要求页面 qiskit.org/documentation/install.html

如何操作...

  1. 创建你的 Anaconda 虚拟环境:

    $ conda create -n environment_name python=3
    

    这将安装一组特定于环境的包。

  2. 激活你的虚拟环境:

    $ conda activate environment_name
    
  3. 确认你处于虚拟环境中。

    你的命令提示符现在应该包含你环境的名字;我用自己的环境 packt_qiskit 做了类似设置:

    (environment_name) … $ to remind you that you must execute the commands in the correct environment. In the rest of the book, I will assume that you are indeed in your Qiskit-enabled environment and just show the generic prompt: $.
    
  4. 如果需要,执行 pip 更新。

    要安装 Qiskit®,你必须使用 pip,因为 Qiskit® 不是以 conda 包的形式发布的。最新的 Qiskit® 需要 pip 19 或更高版本。

    如果你有一个较旧的 pip 版本,你必须使用以下命令进行升级:

    (environment_name) … $  pip  install  -U  pip
    
  5. 安装 Qiskit®。

    因此,在一切准备就绪之后,我们现在可以进入正题,在你的环境中安装 Qiskit® 代码。让我们开始吧!

    (environment_name) … $  pip install qiskit
    

    轮子构建失败

    作为安装的一部分,你可能会看到 wheel 构建失败的错误。这个错误可以忽略。

  6. 使用 Python 验证 Qiskit® 是否成功安装。

    打开 Python:

    (environment_name) … $  python3
    

    导入 Qiskit®:

    >>> import qiskit
    

    这有点令人兴奋;我们将首次使用 Qiskit® 代码。当然,不是用来编程量子计算机,但至少可以确保我们现在已经准备好开始编写你的量子程序。

    列出版本详细信息:

    >>> qiskit.__qiskit_version__
    

    这应该会显示已安装的 Qiskit® 组件的版本:

    {'qiskit-terra': '0.15.2', 'qiskit-aer': '0.6.1', 'qiskit-ignis': '0.4.0', 'qiskit-ibmq-provider': '0.9.0', 'qiskit-aqua': '0.7.5', 'qiskit': '0.21.0'}
    

恭喜,你的 Qiskit® 安装已完成;你现在可以开始了!

通过在你的虚拟环境中使用 pip install,你可以在那个环境中安装 Qiskit®,这样它就会与你的其他 Python 环境保持隔离。

还有更多…

Qiskit® 还附带了一些可选的可视化依赖项,用于在 Qiskit® 组件之间使用可视化。你可以使用以下命令安装这些依赖项:

(environment_name) … $ pip install qiskit[visualization]

注意

如果你使用的是 zsh shell,你必须将组件用引号括起来:pip install 'qiskit[visualization]'

参见

要快速了解 Anaconda 环境,请参阅 Anaconda 文档中的管理环境docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html

这本书在任何方面都不是 Qiskit®安装故障排除指南,而且你可能会在安装时遇到问题,这取决于你的本地操作系统、版本和其他因素。

但不必担心,帮助即将到来。以下是一些友好且有用的频道,你可以寻求帮助:

下载代码样本

这本书中的食谱包括简短和一些不那么简短的样本程序,这些程序将引导你完成编程量子计算机的第一步。如果你想的话,可以直接从书中的说明中输入这些程序,但为了方便,你也可以直接从 Packt Cookbook GitHub 组织获取样本代码。

Python 样本是为 Python 3.5+编写的,以及你在 Python 环境中安装的 Qiskit®扩展。所有的 Python 样本都有.py扩展名。

准备工作

虽然你可以直接在你的 Python 环境中输入食谱,或者在 IBM Quantum Experience®上的 Jupyter Notebooks 或者你本地的 Anaconda 环境中输入,但使用 Git 下载或克隆样本代码到你的本地环境会更有效率。克隆的优势在于,如果远程仓库有任何更新,你可以稍后从远程仓库刷新你的本地文件。

如果你没有计划使用 Git,而是要下载作为压缩文件的食谱,请继续阅读如何操作

要使用 Git 克隆样本代码,你必须首先做以下事情:

  1. 获取 GitHub 账户。这些是免费的,你可以在github.com注册一个。

  2. 在你的本地环境中安装 Git。更多信息,请参阅git-scm.com/book/en/v2/Getting-Started-Installing-Git

  3. 如果你是一名用户界面人员,你可能还希望安装 GitHub Desktop,可在此处获取:desktop.github.com/.

如何操作...

你有几种不同的选项可以将样本食谱下载到你的本地机器。

对于每个,首先打开您的网页浏览器,然后转到github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience上的Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience GitHub 仓库。

将仓库作为压缩文件下载

获取食谱的最简单方法是将样本文件作为压缩目录抓取,并在您的本地机器上解压缩:

  1. 在前面的 URL 中,点击克隆或下载按钮并选择下载 zip

  2. 下载压缩文件并选择位置。

  3. 解压缩文件。

使用 git 克隆仓库

  1. 点击克隆或下载按钮并复制 URL。

  2. 打开您的命令行,导航到您想要克隆目录的位置。

  3. 输入以下命令:

    $ git clone **https://github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience.git**
    

    命令应该产生以下类似的结果:

    Cloning into 'Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience'...
    remote: Enumerating objects: 250, done.
    remote: Counting objects: 100% (250/250), done.
    remote: Compressing objects: 100% (195/195), done.
    remote: Total 365 (delta 106), reused 183 (delta 54), pack-reused 115
    Receiving objects: 100% (365/365), 52.70 MiB | 5.42 MiB/s, done.
    Resolving deltas: 100% (153/153), done.
    

使用 GitHub Desktop 克隆仓库

  1. 点击克隆或下载按钮,并选择在桌面打开

  2. 在 GitHub Desktop 对话框中,选择要克隆仓库的目录并点击确定

您现在可以浏览这本烹饪书中的食谱。每一章都包含一个或多个食谱。如果您愿意,可以直接将食谱复制粘贴到您的 Python 环境中,或者 IBM 量子体验®或您的本地 Anaconda 环境中。

打开食谱文件

到目前为止,您已经使用命令行完成了所有操作。那么,您是否想抓取以下 Python 程序,并从您喜欢的 Python 解释器中运行它,例如Anaconda SpyderJupyter Notebooks

如果您已下载样本文件,食谱文件可在以下本地目录中找到:

<The folder where you downloaded the files>/https://github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter01/ch1_r1_version.py

ch1_r1_version.py代码示例列出了我们刚刚安装的 Qiskit®组件的版本号:

# Import Qiskit
import qiskit
# Set versions variable to the current Qiskit versions
versions=qiskit.__qiskit_version__
# Print the version number for the Qiskit components
print("Qiskit components and versions:")
print("===============================")

for i in versions:
    print (i, versions[i]) 

运行时,前面的代码应该产生以下类似的结果:

图 1.2 – Qiskit®组件及其版本列表

图 1.2 – Qiskit®组件及其版本列表

以下部分涵盖了如何在可用的环境中运行脚本。

Spyder 中的 Python 脚本

在您的本地环境中,您现在可以打开您选择的 Python 解释器中的 Python 脚本;例如,Anaconda 中包含的 Spyder:

重要

确保您在安装 Qiskit®的虚拟环境中运行解释器。否则,它将无法找到 Qiskit®,程序将无法正确运行。

  1. 打开您的 Anaconda 用户界面。

  2. 选择您的虚拟环境。

  3. 点击Spyder磁贴。如果您的虚拟环境中尚未安装 Spyder,它将现在进行安装。这可能需要一些时间。请耐心等待!

  4. 在 Spyder 中,打开本章包含的 Python 脚本:

    <The folder where you downloaded the files>/https://github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter01/ch1_r1_version.py
    
  5. 点击运行。脚本现在将提取已安装的 Qiskit®组件的版本号。您也可以在 Jupyter 笔记本中打开 Python 脚本,例如,在在线 IBM Quantum Experience®环境中,但这需要额外的工作。

Anaconda 中的 Jupyter 笔记本

  1. 打开您的 Anaconda 用户界面。

  2. 选择您的虚拟环境。

  3. 点击Jupyter 笔记本磁贴。如果 Jupyter Notebook 尚未为您的虚拟环境安装,它现在将安装。

  4. 您的默认网页浏览器将在您的根目录中打开。浏览并打开以下内容:

    <The folder where you downloaded the files>/https://github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter01/ch1_r1_version.py
    
  5. 示例脚本在 Jupyter 文本编辑器中打开。您现在可以看到代码,但不能运行它。

  6. 返回 Jupyter 浏览器并点击新建 | 笔记本

  7. 将 Python 脚本代码复制并粘贴到新笔记本中。现在您可以点击运行并查看代码执行。

IBM Quantum Experience®中的 Jupyter 笔记本

  1. 要在在线 IBM Quantum Experience®笔记本中运行 Python 脚本,请登录到quantum-computing.ibm.com/login

  2. 在主仪表板上,点击左侧菜单中的Quantum Lab图标(A picture containing drawing    描述自动生成),然后点击新建笔记本并按照我们在“Anaconda 中的 Jupyter 笔记本”部分讨论的过程进行操作:

Figure 1.3 – 在 IBM Quantum Experience®上运行您的 Qiskit®代码

Figure 1.3 – 在 IBM Quantum Experience®上运行您的 Qiskit®代码

它是如何工作的...

我们将在后续章节中介绍的基于 Qiskit®的 Python 代码可以在满足 Qiskit®要求的任何 Python 环境中运行,因此您可以自由选择适合您的环境。并且有了这个环境,您也可以自由选择您喜欢的工具来运行程序。

对于这本书,我已经测试了在Anaconda标准提供的Spyder编辑器和 IBM Quantum Experience®和 Anaconda 上的Jupyter Notebook环境中运行代码。

安装您的 API 密钥并访问您的提供商

现在您已经安装了 Qiskit®,您可以立即开始创建您的量子程序并在本地模拟器上运行这些程序。但是,如果您在某个时候想在实际的 IBM Quantum®硬件上运行您的量子代码,您必须在本地上安装您自己的唯一 API 密钥。

IBM Quantum Experience®上的 API 密钥

如果您在 IBM Quantum Experience®笔记本环境中运行 Qiskit®程序,您的 API 密钥将自动注册。

准备工作

在您安装 API 密钥之前,您必须首先拥有一个 IBM Quantum Experience®账户。如果您还没有创建,请返回并创建(见“创建您的 IBM Quantum Experience®账户”部分)。

如何操作...

让我们看看如何在本地安装 API 密钥:

  1. quantum-computing.ibm.com/login登录 IBM Quantum Experience®。

  2. 在 IBM Quantum Experience®仪表板上,找到右上角的用户图标,点击它,然后选择我的账户

  3. 在账户页面,找到本地环境中的 Qiskit部分,并点击复制令牌

  4. 您现在可以将令牌粘贴到临时位置或将其保持在剪贴板中。

  5. 在您的本地机器上,访问您的 Qiskit®环境。我们已经完成了这个步骤,但这里重复一下过程,如果您正在使用 Anaconda。

  6. 激活您的虚拟环境:

    $ conda activate environment_name
    
  7. 打开 Python:

    $(environment_name) … $  python3
    

    验证 Python 头部是否显示,并且您正在运行正确的 Python 版本:

    Python 3.7.6 (default, Jan  8 2020, 13:42:34) 
    [Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> 
    
  8. 获取所需的IBMQ类:

    >>> from qiskit import IBMQ		
    
  9. 在本地安装您的 API 令牌:

    >>> IBMQ.save_account('MY_API_TOKEN')
    

    在这里,将MY_API_TOKEN替换为您刚刚从 IBM Quantum Experience®复制的 API 令牌:请保持单引号不变,因为它们对于命令是必需的。

  10. 加载您的账户。

    现在令牌已经就位,让我们验证一切是否正常,并且您的账户具有正确的权限:

    >>> IBMQ.load_account()
    

    这应该会显示以下输出:

    <AccountProvider for IBMQ(hub='ibm-q', group='open', project='main')>
    

    这是您账户的提供者信息,包括hubgroupproject

它是如何工作的...

您为此练习导入的主要类是IBMQ,这是一个用于在云中由 IBM 提供的量子硬件和软件的工具箱。

在本章中,我们使用了save.account()来在本地存储您的账户。随着我们继续前进,在我们将要访问 IBM Quantum®机器的食谱中,我们将使用IBMQ.load_account()IBMQ.get_provider()类来确保您具有正确的访问权限。

更新您的 API 密钥

如果出于某种原因,您需要在 IBM Quantum Experience®上创建新的 API 令牌并更新本地保存的令牌,您可以使用以下命令:

>>> IBMQ.save_account('NEW_API_TOKEN', overwrite=True)

还有更多...

在本食谱中的后续代码中,我们将使用以下命令设置provider变量以保存您账户的提供者信息:

>>> provider = IBMQ.get_provider()

我们可以使用provider信息在选择 IBM Quantum®计算机或后端时运行您的量子程序。在以下示例中,我们选择了一个名为ibmqx2的量子计算机作为我们的后端。该机器的内部引用是ibmqx2

>>> backend = provider.get_backend('ibmqx2')

保持您的 Qiskit®环境更新

Qiskit®是一个开源编程环境,它处于持续变化中。在撰写本书的过程中,我经历了许多软件的次要和主要版本更新。

通常来说,保持最新版本是一个好主意,但有些更新可能会改变代码组件的行为。查看每个新版本的发布说明总是一个好主意。有时会引入一些更改,这些更改将改变您的代码的行为方式。在这些情况下,您可能想要在验证代码仍然按预期工作之后再进行升级。

如果你使用 Anaconda 环境,那么你可以维护多个不同 Qiskit® 级别的环境,以便在升级后的 Qiskit® 版本破坏你的代码时有一个回退环境。

Qiskit® 发展迅速

IBM Quantum Experience® 笔记本环境始终运行 Qiskit® 的最新版本,在你升级本地环境之前,在该环境中测试你的代码可能是个好主意。

你还可以订阅通知更新,以了解何时提供了新版本:

  1. quantum-computing.ibm.com/login 登录 IBM Quantum Experience®。

  2. 在 IBM Quantum Experience® 仪表板上,找到右上角的用户图标,点击它,然后选择 我的账户

  3. 在账户页面下,在 通知 设置中,将 更新和新功能公告 设置为 开启

准备工作

在开始之前,验证你每个环境(如果你有多个)正在运行的 Qiskit® 版本。

对于每个环境,从命令行、IDE(如 Spyder)或 Jupyter 笔记本启动 Python,然后执行以下代码:

>>> import qiskit
>>> qiskit.__qiskit_version__

如果你安装了旧版本的 Qiskit®,前面的代码可能会产生以下输出:

{'qiskit-terra': '0.9.0', 'qiskit-aer': '0.3.0', 'qiskit-ibmq-provider': '0.3.0', 'qiskit-aqua': '0.6.0', 'qiskit': '0.12.0'}

然后,你可以前往 Qiskit® 发布说明以了解是否有更新版本的可用:qiskit.org/documentation/release_notes.html

这需要很多步骤来确保。整个过程在 Python 中是自动化的。要进入这条路径,请参阅下一节。

如何操作...

  1. 激活你的虚拟环境:

    $ conda activate environment_name
    
  2. 运行以下命令以检查你的虚拟环境中的过时 pip 包:

    (environment_name) … $  pip list --outdated
    
  3. 这将返回一个列表,列出你当前过时的所有 pip 包以及可用的版本:

    Example:Package                  Version  Latest   Type 
    ------------------------ -------- -------- -----
    …
    qiskit                   0.19.6   0.21.0   sdist
    qiskit-aer               0.5.2    0.6.1    wheel
    qiskit-aqua              0.7.3    0.7.5    wheel
    qiskit-ibmq-provider     0.7.2    0.9.0    wheel
    qiskit-ignis             0.3.3    0.4.0    wheel
    qiskit-terra             0.14.2   0.15.1   wheel 
    …
    
  4. 然后,使用 pip 更新 Qiskit® 就变得非常简单:

    (environment_name) … $  pip install qiskit --upgrade
    
  5. 在命令行中,验证 Qiskit® 是否已安装:

    (environment_name)… $ pip show qiskit
    

    这将产生类似于以下输出的结果:

    Name: qiskit
    Version: 0.21.0
    Summary: Software for developing quantum computing programs
    Home-page: https://github.com/Qiskit/qiskit
    Author: Qiskit Development Team
    Author-email: qiskit@us.ibm.com
    License: Apache 2.0
    Location: /Users/hassi/opt/anaconda3/envs/packt_qiskit/lib/python3.7/site-packages
    Requires: qiskit-aer, qiskit-terra, qiskit-aqua, qiskit-ignis, qiskit-ibmq-provider
    Required-by: 
    …
    
  6. 验证 Qiskit® 是否已集成到你的隔离环境中。

    打开 Python:

    (environment_name)… $ python3
    

    导入 Qiskit®:

    >>> import qiskit
    

    列出版本详细信息:

    >>> qiskit.__qiskit_version__
    

    这应该会显示已安装的 Qiskit® 组件的版本:

    {'qiskit-terra': '0.15.2', 'qiskit-aer': '0.6.1', 'qiskit-ignis': '0.4.0', 'qiskit-ibmq-provider': '0.9.0', 'qiskit-aqua': '0.7.5', 'qiskit': '0.21.0'} 
    

恭喜,你的 Qiskit® 升级成功了;你现在正在运行最新的代码!

它是如何工作的...

根据你如何消费这本书,你可能会在第一次阅读过程中查看此过程,并且没有可用的升级。如果是这样,请继续将此菜谱添加到书签,并在 Qiskit® 升级后稍后再回来。

pip 工具将为你管理每个虚拟环境的升级。如我之前提到的,如果你有多个环境,进行分阶段升级可能是个好主意。

例如,你可以升级一个环境,并在该环境中测试运行你的量子程序,以确保新版本没有破坏你的代码。

好的,有了这些,你应该已经准备好了一个或多个 Qiskit®环境,可以在这些环境中运行你的量子程序。如果你感觉准备好了,现在就可以直接跳到第四章从 Terra 开始入门,开始使用 Qiskit®在 Python 中编写量子程序。如果你想要做一些准备工作来感受一下量子计算机编程是什么样的,可以从第二章使用 Python 进行量子计算和量子比特入门,来了解量子比特和门的基础知识,或者从第三章IBM Quantum Experience® – 量子拖放,通过使用 IBM Quantum Experience®的拖放编程界面来获得量子程序的直观感受。

无论你选择哪条路径,不用担心,我们会让 Python 来做艰苦的工作。再次强调,享受编程的乐趣!

第二章:第二章:使用 Python 进行量子计算和量子比特

量子计算是一个既新又旧的领域。用于实现量子计算(如量子力学的叠加和纠缠)的思想和概念已经存在了近一个世纪,量子信息科学领域几乎是在 40 年前成立的。早期的探索者,如 Peter Shor 和 Lov Grover,产生了量子计算算法(Shor 算法和 Grover 算法),这些算法现在开始像基础物理概念(如 E=mc**2)一样为人所熟知。有关详细信息,请参阅本章末尾的参考文献。

同时,真正利用这些效果的量子计算机是一种相对较新的发明。在 1990 年,DiVincenzo 概述了构建一个量子计算机的要求,IBM 于 2016 年推出了 IBM Quantum Experience® 和 Qiskit®,这实际上是第一次任何人(除了研究实验室的人)可以真正开始探索这个新兴领域。

那么,经典计算和量子计算之间有什么区别呢?一种开始探索的方法是看看每个计算所使用的基本计算元素——经典比特和量子量子比特。

在本章中,我们将对比比特和量子比特,通过一些通用的线性代数来更详细地探索它们,并对比确定性(经典)计算和概率性(量子)计算。我们甚至会快速浏览一些基本的 Qiskit® 展示方法来可视化量子比特。

在本章中,我们将介绍以下食谱:

  • 比较比特和量子比特

  • 在 Python 中可视化量子比特

  • 量子门的快速介绍

技术要求

本章中讨论的食谱可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter02

有关如何获取示例代码的更多信息,请参阅第一章中的下载代码示例部分,准备你的环境

比较比特和量子比特

那么,让我们从显而易见——或者也许不那么显而易见——的观念开始,即大多数阅读这本书的人都知道比特是什么。

我们有一种直观的感觉,即比特要么是0),要么是1)。通过将许多比特组合在一起,你可以创建字节以及任意大的二进制数,并且利用这些数,你可以构建最令人惊叹的计算机程序,编码数字图像,加密你的情书和银行交易,等等。

在经典计算机中,位是通过在组成逻辑板的晶体管上使用低电压或高电压来实现的,通常是 0 V 和 5 V 这样的事情。在硬盘驱动器中,位可能是一个以某种方式磁化的区域,用来表示 0,而另一种方式表示 1,等等。

在关于量子计算的书籍中,需要强调的重要观点是,经典位只能是 0 或 1;它永远不能是其他任何东西。在计算机的例子中,你可以想象一个带有输入和输出的盒子,盒子代表你正在运行的程序。在经典计算机(我在这里使用“经典”一词来表示不是量子计算机的二进制计算机)中,输入是一串位,输出是另一串位,盒子是一系列位被操作、按摩和组织以生成该输出的算法。再次强调的一个重要事情是,在盒子中,位仍然是位,始终是 0 或 1,没有其他。

量子比特,正如我们将在本章中发现的那样,是相当不同的东西。让我们去探索。

准备工作

作为食谱来说,这个并不真正值得炫耀。它只是一个快速的 Python 和 NumPy 实现,将位定义为 2x1 矩阵,或者表示 0 或 1 的向量。我们还引入了狄拉克符号 img/Formula_02_001.png 来表示我们的量子比特。然后我们计算测量位和量子比特时得到各种结果的概率。

以下食谱的 Python 文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter02/ch2_r1_bits_qubits.py

如何做到这一点...

  1. 让我们先导入 numpymath,这是我们进行计算所需的:

    import numpy as np
    from math import sqrt, pow
    
  2. 为 0、1、img/Formula_02_001.png 创建并打印位和量子比特向量,分别为 [1,0][0,1][1,0][0,1][a,b]

    # Define the qubit parameters for superposition
    a = sqrt(1/2)
    b = sqrt(1/2)
    if round(pow(a,2)+pow(b,2),0)!=1:
        print("Your qubit parameters are not normalized.
            \nResetting to basic superposition")
        a = sqrt(1/2)
        b = sqrt(1/2)
    bits = {"bit = 0":np.array([1,0]), 
        "bit = 1":np.array([0,1]), 
        "|0\u27E9":np.array([1,0]), 
        "|1\u27E9":np.array([0,1]), 
        "a|0\u27E9+b|1\u27E9":np.array([a,b])}
    # Print the vectors 
    for b in bits:
      print(b, ": ", bits[b].round(3)) 
    print ("\n")
    

    注意这里的 Unicode 条目:\u27E9。我们使用这个而不是简单的 > 来在输出中创建看起来很棒的狄拉克量子比特渲染 img/Formula_02_003.png

    您必须提供正确的 a 和 b 参数

    注意参数验证代码检查 ab 的值是否 归一化。如果不是,则将 ab 重置为简单的 50/50 超叠加,通过设置 img/Formula_02_004.png 和 ![img/Formula_02_005.png]。

  3. 通过创建测量字典来测量量子比特,然后计算从我们创建的位向量中得到 01 的概率:

    print("'Measuring' our bits and qubits")
    print("-------------------------------")
    prob={}
    for b in bits:
        print(b)
        print("Probability of getting:")
        for dig in range(len(bits[b])):
            prob[b]=pow(bits[b][dig],2)
            print(dig, " = ", '%.2f'%(prob[b]*100), percent") 
        print ("\n")
    

上述代码应产生以下输出:

![图 2.1 – 使用 NumPy 模拟位和量子比特img/Figure_2.1_B14436.jpg

图 2.1 – 使用 NumPy 模拟位和量子比特

现在我们知道了在测量比特和量子比特时得到01值的概率。对于其中一些(0,1,),结果是我们可以预期的,0 或 100%;比特或量子比特要么是 0 要么是 1,没有其他。对于其中一个(),这是一个处于 0 和 1 叠加状态的量子比特,得到任一的概率是 50%。这是一个对于经典比特永远不会发生的结果,只有对于量子比特。我们将在下一节中解释原因。

它是如何工作的...

在这个配方中,我们看到的经典比特读取的概率总是100%,要么是 0 要么是 1;没有其他选项。但对于可以表示为的量子比特,0 或 1 的概率与成正比。对于纯状态,ab总是 1,测量每个的概率都是 100%。但对于我们标记为的量子比特,a 和 b 都是,给出 0 或 1 的概率为 50%

测量比特和量子比特

在经典计算和量子计算中,“测量”这个词意味着两件略有不同的事情。在经典计算中,你可以在任何时候测量你的比特,而不会严重干扰你正在进行的计算。对于量子计算机来说,测量是一个更明确的行为,导致你的量子比特从表现出量子力学行为的比特转变为表现出经典行为的比特。测量量子比特后,你就完成了。你不能再对该量子比特进行任何进一步的量子操作。

由于量子比特的量子力学性质,我们可以将其描述为与用于比特的向量相似的向量。为了澄清这一点,当我们谈论量子比特时,我们不仅使用 0 和 1 作为标签,而是使用狄拉克矢量符号,,表示这些是向量空间中的状态向量。

我们可以像这样写出量子比特的状态向量,(psi):

  • 对于处于基态的量子比特,表示 0

  • 对于处于激发态的量子比特,表示 1

在这里,我们使用了基态激发态作为对量子比特进行分类的一种方式。这是合适的,因为 IBM Quantum®量子比特使用的约瑟夫森结是具有两个能级的量子系统。根据底层物理系统,量子比特也可以基于其他两能级量子系统,例如电子自旋(向上或向下)或光子偏振(水平或垂直)。

到目前为止,与经典比特相比,并没有什么直观上的不同,因为每个都只代表值 0 或 1。但现在我们增加了一个复杂性:一个量子比特也可以是两种状态的叠加,

公式,其中ab是复数。这些数字被归一化,使得公式,这在几何上意味着结果向量公式的长度为 1。这很重要!

回到最简单的例子,这些可以描述如下:

  • 公式对于基态的量子比特。在这种情况下,a=1,b=0。

  • 公式对于激发态的量子比特。在这种情况下,a=0,b=1。

到目前为止,一切顺利,但现在我们加入量子转折:叠加。以下量子比特状态向量也得到支持:

公式

只是为了检查我们是否仍然归一化,在这种情况下,

公式.

但这个状态向量意味着什么呢?

量子比特被设置在一个状态,它正好位于公式公式之间;它存在于两种基本状态的叠加中。它正在表现出量子行为。

重要提示

量子比特的量子叠加态只能在我们在量子计算机上进行计算时维持。对于自然界中的实际粒子,例如表现出量子行为的光子,也是如此。例如,光子的偏振可以描述为在光子飞行过程中水平方向和垂直方向的叠加,但当你在其路径上添加一个偏振器时,你会测量到它是水平方向或垂直方向,而不会是其他方向。

回到计算机作为盒子的例子,对于量子计算机,我们有一个类似的概念,输入是一串比特,输出也是另一串比特。区别在于盒子内部,在计算过程中,量子比特可以处于叠加态。

然而,当我们测量量子比特以获得那一串输出比特时,量子比特必须量子力学地决定它们是公式还是公式,这就是那些ab参数出现的地方。

公式公式不仅表明该向量被归一化到长度 1,而且还描述了获得公式公式输出的概率。获得公式的概率是公式公式公式。这是量子计算机和经典计算机之间差异的核心。量子计算机是概率性的——你无法预先知道最终结果会是什么,只能知道得到某个结果的概率——而经典计算机是确定性的——你总是可以,至少在理论上,预测出答案会是什么。

关于概率计算

人们经常对量子计算机和概率结果感到有些困惑,并将整个量子编程概念可视化为一个量子位在所有不同状态下随机且无法控制地旋转。这不是一个真实的画面;每个量子位都是初始化在特定的已知状态,,然后通过量子门操作进行作用。每次操作都是严格确定性的;没有任何随机性。在量子状态演化的每个阶段,我们都能确切地知道我们的量子位在做什么,这可以用一个叠加来表示。只有在最后,当我们测量并迫使量子位变为 0 或 1 时,概率性质才会显现出来,测量的 0 或 1 的概率由ab参数()设定。

参见

关于量子位及其解释的更多信息,请参阅以下优秀书籍:

  • 与量子位共舞:量子计算是如何工作的以及它如何改变世界,罗伯特·S·萨托,Packt Publishing Ltd.,2019 年,第七章一个量子位

  • 量子计算与量子信息,艾萨克·L·丘昂;迈克尔·A·尼尔森,剑桥大学出版社,2010 年,第 1.2 章量子比特

  • 量子力学:理论最小值,莱昂纳德·苏斯金德 & 阿特·弗里德曼,基础书籍出版社,2015 年,讲座 1:系统和实验

  • 肖尔,我会做到的,斯科特·阿伦森的博客,www.scottaaronson.com/blog/?p=208

  • 量子电话簿是什么?,洛夫·格罗弗,朗讯科技公司,web.archive.org/web/20140201230754/http://www.bell-labs.com/user/feature/archives/lkgrover/

  • 量子计算的物理实现,大卫·P·迪文森佐,IBM,arxiv.org/abs/quant-ph/0002077

在 Python 中可视化量子位

在这个菜谱中,我们将使用通用的 Python 和 NumPy 创建一个比特的矢量和可视化表示,并展示它只能处于两种状态,0 和 1。我们还将通过展示量子位不仅可以处于唯一的 0 和 1 状态,还可以处于这些状态的叠加,来引入我们对 Qiskit®世界的第一次小规模探索。要做到这一点,我们需要将量子位取为矢量形式,并将其投影到所谓的布洛赫球上,Qiskit®提供了一个方法。让我们开始工作吧!

在前面的菜谱中,我们通过两个复数参数——ab的帮助定义了我们的量子位。这意味着我们的量子位可以取除了经典比特的 0 和 1 之外的值。但即使你知道ab,也很难想象一个介于 0 和 1 之间的量子位。

然而,通过一点数学技巧,我们发现也可以用两个角度来描述一个量子比特——theta () 和 phi ()——并在布洛赫球上可视化量子比特。你可以把 角度想象成地球的纬度和经度。在布洛赫球上,我们可以投射量子比特可能取的任何值。

变换的方程如下:

这里,我们使用之前看到的公式:

ab 分别如下:

我会把更深入细节和数学留给你们去进一步探索:

图 2.2 – 布洛赫球

图 2.2 – 布洛赫球。

糟糕的经典比特在布洛赫球上无法做太多事情,因为它们只能存在于南北极,代表二进制值 0 和 1。我们只包括它们来进行比较。

0 指向上方和 1 指向下方有特殊的历史原因。 的量子比特向量表示为 ,或向上,,或向下,这不符合直观的预期。你会认为 1,或者一个更令人兴奋的量子比特,应该是一个指向上方的向量,但事实并非如此;它指向下方。所以,我也会用同样糟糕的经典比特来做:0 表示向上,1 表示向下。

纬度,从穿过布洛赫球的切面到两极的距离,对应于 ab 的数值, 指向直上方的 (a=1,b=0), 指向直下方的 ,对于基本叠加态 来说,它指向赤道。

因此,我们添加到方程中的是量子比特的相位 角度不能直接测量,并且对我们的初始量子电路的结果没有影响。在本书后面的第九章,“Grover 搜索算法”中,我们将看到你可以利用相位角度在某些算法中取得很大的优势。但我们现在走得太远了。

准备工作

以下菜谱的 Python 文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter02/ch2_r2_visualize_bits_qubits.py

如何操作...

对于这个练习,我们将使用公式 2.57公式 2.58角度作为 Bloch 球面上的纬度和经度坐标。我们将使用相应的角度来编码 0、1、公式 2.59公式 2.049公式 2.061状态。由于我们可以将这些角度设置为任何纬度和经度值,我们可以将量子比特状态向量放置在 Bloch 球面上的任何位置:

  1. 导入我们需要的类和方法,包括来自 Qiskit®的numpyplot_bloch_vector。我们还需要使用cmath来对复数进行一些计算:

    import numpy as np
    import cmath 
    from math import pi, sin, cos
    from qiskit.visualization import plot_bloch_vector
    
  2. 创建量子比特:

    # Superposition with zero phase
    angles={"theta": pi/2, "phi":0}
    # Self defined qubit
    #angles["theta"]=float(input("Theta:\n"))
    #angles["phi"]=float(input("Phi:\n"))
    # Set up the bit and qubit vectors
    bits = {"bit = 0":{"theta": 0, "phi":0}, 
        "bit = 1":{"theta": pi, "phi":0}, 
        "|0\u27E9":{"theta": 0, "phi":0}, 
        "|1\u27E9":{"theta": pi, "phi":0}, 
        "a|0\u27E9+b|1\u27E9":angles}
    

    从代码示例中,你可以看到我们现在只使用 theta 角度,theta = 0 表示我们直接向上指,theta 公式 2.062表示直接向下,对于我们的基本比特和量子比特:0、1、公式 2.063公式 2.064。theta 公式 2.065将我们带到赤道,我们使用这个角度来表示叠加量子比特公式 2.066

  3. 打印 Bloch 球面上的比特和量子比特。

    Bloch 球面方法接受一个三维向量作为输入,但我们必须首先构建这个向量。我们可以使用以下公式来计算用于plot_bloch_vector和显示比特和量子比特的 Bloch 球面表示的XYZ参数:

    bloch=[cos(bits[bit]["phi"])*sin(bits[bit]
        ["theta"]),sin(bits[bit]["phi"])*sin(bits[bit]
        ["theta"]),cos(bits[bit]["theta"])]
    

    我们现在遍历比特字典,显示比特和量子比特的 Bloch 球面视图以及相应的状态向量:

    状态向量是使用我们之前看到的方程计算的:

    我们现在看到的是,ab实际上可以变成复数值,正如定义的那样。

    这里是代码:

    for bit in bits:
         bloch=[cos(bits[bit]["phi"])*sin(bits[bit]
            ["theta"]),sin(bits[bit]["phi"])*sin(bits[bit]
            ["theta"]),cos(bits[bit]["theta"])]
        display(plot_bloch_vector(bloch, title=bit))
        # Build the state vector
        a = cos(bits[bit]["theta"]/2)
        b = cmath.exp(bits[bit]["phi"]*1j)*sin(bits[bit]
            ["theta"]/2)
        state_vector = [a * complex(1, 0), b * complex(1, 0)]
        print("State vector:", np.around(state_vector, 
            decimals = 3))
    
  4. 样本代码应该给出类似于以下示例的输出。

    首先,我们展示经典比特,0 和 1:

    图 2.3 – 经典比特的 Bloch 球面可视化

    图 2.3 – 经典比特的 Bloch 球面可视化

  5. 然后,我们展示量子比特,或称为量子比特,公式 2.30公式 2.29图 2.4 – 量子比特的 Bloch 球面可视化

    图 2.4 – 量子比特的 Bloch 球面可视化

  6. 最后,我们展示一个处于叠加状态的量子比特,它是公式 2.073公式 2.074的混合:

图 2.5 – 超叠加态的量子比特的 Bloch 球面可视化

图 2.5 – 超叠加态的量子比特的 Bloch 球面可视化

因此,简单的 0、1、公式 2.075公式 2.076显示并没有什么震撼人心的地方。它们只是根据需要指向 Bloch 球面的北极和南极。如果我们通过测量来检查比特或量子比特的值,我们将以 100%的确定性得到 0 或 1。

另一方面,以计算的超置量子比特指向赤道。从赤道开始,到两个极点的距离相等,因此得到 0 或 1 的概率是 50/50。

在代码中,我们包括以下几行,这些行定义了angles变量,它为量子比特设置了

# Superposition with zero phase
angles={"theta": pi/2, "phi":0}

还有更多...

我们之前提到过,我们不会涉及相位()角,至少最初不会。但我们可以可视化它对我们量子比特的作用。记住,我们可以直接使用角度来描述ab

要测试这一点,你可以取消注释样本代码中定义角度的行:

# Self-defined qubit
angles["theta"]=float(input("Theta:\n"))
angles["phi"]=float(input("Phi:\n"))

你现在可以通过操作值来定义你的第三个量子比特的外观。让我们通过再次运行脚本并输入一些角度来测试我们能做什么。

例如,我们可以尝试以下操作:

你应该看到最终的布洛赫球看起来像以下这样:

图 2.6 – 通过

图 2.6 – 通过 公式 2.6 旋转的量子比特状态向量

注意状态向量仍然位于赤道上的,但现在与x轴成角度。你还可以查看状态向量:[0.707+0.j 0.653+0.271j]。

我们现在已经离开了布洛赫球上的本初子午线,进入了复平面,并添加了一个相位角,它由沿y轴的虚状态向量分量表示:

让我们继续旅行

尝试使用不同的角度来获取其他ab条目,并看看你最终会到达哪里。对于这些粗略估计,不需要包括 10+位小数,两位或三位小数就足够了。尝试在你的家乡在布洛赫球上绘制。记住,脚本需要以弧度为单位输入,而 theta 从北极开始,而不是赤道。例如,英国格林尼治天文台的坐标是 51.4779° N,0.0015° W,这转化为:

这里是 Qiskit®和一个地球仪显示相同的坐标:

图 2.7 – 格林尼治量子化和在地球仪上

图 2.7 – 格林尼治量子化和在地球仪上

参见

量子计算与量子信息,艾萨克·L·丘恩;迈克尔·A·尼尔森,剑桥大学出版社,2010 年,第 1.2 章量子比特,以及第 4.2 章单量子比特操作

量子门的快速介绍

现在我们已经区分了比特和量子比特的区别,并且也已经了解了如何将量子比特可视化成布洛赫球,我们知道关于量子比特的所有知识,对吗?嗯,并不完全是这样。量子比特,或者更确切地说,成百上千个量子比特,并不是构建量子计算机的唯一东西!你需要对量子比特进行逻辑操作。为此,就像经典计算机一样,我们需要逻辑门。

我不会详细介绍逻辑门的工作原理,但可以说,量子门操作一个或多个量子比特的输入,并输出一个结果。

在这个菜谱中,我们将通过使用单比特和多比特门的矩阵乘法来逐步解释少数量子门的数学意义。别担心,我们不会深入挖掘,只是浅尝辄止。你将在第六章中找到更深入的量子门内容,理解 Qiskit 门库

再次强调,我们目前不会构建任何实际的 Qiskit 量子电路。我们仍在使用或多或少纯 Python 和一些 NumPy 矩阵操作来证明我们的观点。

准备工作

以下菜谱的 Python 文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter02/ch2_r3_qubit_gates.py.

如何操作...

这个菜谱将创建量子比特和门的向量和矩阵表示,并使用简单的代数来说明量子比特在门作用下的行为:

  1. 在你的 Python 环境中运行ch2_r3_qubit_gates.py,并响应按回车键继续提示以继续程序。

  2. 首先,我们看到了三个量子比特状态的向量表示:![Figure 2.8 – 量子比特作为向量 Figure 2.8 – 量子比特作为向量

    图 2.8 – 量子比特作为向量

  3. 接下来,我们显示了一些门的矩阵表示。

    我们将使用Id(无操作)、X(翻转量子比特)和H(创建叠加)门:

    ![Figure 2.9 – 单量子比特门作为矩阵 Figure 2.9 – 单量子比特门作为矩阵

    图 2.9 – 单量子比特门作为矩阵

  4. 我们单量子比特设置的最终步骤是查看每个门如何操作量子比特。

    这是通过量子比特向量和门矩阵的矩阵乘法来实现的:

    ![Figure 2.10 – 对量子比特起作用的门 Figure 2.10 – 对量子比特起作用的门

    图 2.10 – 对量子比特起作用的门

  5. 单量子比特的工作完成之后,我们现在转向处理两个量子比特状态的组合:![Figure 2.11 – 两个量子比特作为向量 Figure 2.11 – 两个量子比特作为向量

    图 2.11 – 两个量子比特作为向量

  6. 就像单个量子比特一样,我们现在展示两个量子比特量子门的矩阵表示。

    这里,我们使用 CX(受控非,如果另一个量子比特为 1 则翻转一个量子比特)和 swap(交换两个量子比特的值):

    ![图 2.12 – 二量子比特门作为矩阵 图片

    图 2.12 – 二量子比特门作为矩阵

  7. 最后,让我们看看我们的多量子比特状态的门操作。

    同样,我们有量子比特向量和门矩阵的矩阵乘法:

![图 2.13 – 多量子比特门作用于两个量子比特图片

图 2.13 – 多量子比特门作用于两个量子比特

那就这样……我们现在已经见证了 Python 生成的线性代数,它描述了我们的量子比特是如何定义的,以及当门应用于它们时它们是如何表现的。

它是如何工作的……

前一节包含了很多打印信息,但对我们如何得到这些结果解释得很少。让我们深入查看示例代码,看看输出是如何生成的:

提示

下面的编号步骤对应于前一节 如何做 部分中的相同编号步骤。请参考那些步骤,以查看以下代码示例的结果。

  1. 让我们先导入我们需要的数学工具:

    import numpy as np
    from math import sqrt
    
  2. 设置我们量子比特的基本向量。

    将量子比特设置为值 0 时,在狄拉克矢量记法中标记为公式,如果设置为 1,则数学上表示为公式向量,或者设置为公式向量。到目前为止,一切顺利,仍然是 0 和 1。正如我们所见,真正的魔法出现在量子比特设置为表示指向 Bloch 球赤道的叠加值时,或者在任何除了极点的地方——例如,公式,它将被以下向量表示:

    公式

    np.array([1,0])
    

    在这个示例中,我们创建了一个量子比特字典:

    qubits = {"|0\u27E9":np.array([1,0]), 
        "|1\u27E9":np.array([0,1]), 
        "(|0\u27E9+|1\u27E9)/\u221a2":1/sqrt(2)*np.
        array([1,1])}
    for q in qubits:
      print(q, "\n", qubits[q].round(3))
    
  3. 设置我们量子门的基矩阵。

    对于量子比特,任何单个量子比特门都可以用一个 2x2 矩阵来表示,如下所示:公式

    对于单个量子比特,我们实现的数学是一个矩阵运算,对应于两个操作的真值表,IDNOT(或 X,量子门被称为):

    np.array([[0, 1], [1, 0]])
    

    在这个示例中,我们创建了一个门字典:

    gates ={"id":np.array([[1, 0], [0, 1]]),
        "x":np.array([[0, 1], [1, 0]]), 
        "h":1/sqrt(2)*np.array([[1, 1], [1, -1]])}
    for g in gates:
      print(g, "\n", gates[g].round(3)) 
    
  4. 现在,让我们使用 NumPy 在我们的量子比特上应用定义的门。

    在量子比特上应用一个门可以表示为量子比特和门的向量乘积。以下是在量子比特上应用 X 门的 NumPy 矩阵点乘:

    np.dot(np.array([[0, 1], [1, 0]]), np.array([1,0]))
    

    在我们的示例中,我们逐步遍历我们创建的两个字典,对每个门/量子比特组合应用矩阵乘法:

    for g in gates:
        print("Gate:",g) 
        for q in qubits:
            print(q,"\n",qubits[q].round(3),"->", 
                np.dot(gates[g],qubits[q]).round(3))
    

    在这里,我们看到门在我们量子比特上的预期行为:ID 门不做任何事情,X 门翻转量子比特,H 门创建或取消一个叠加。

    如果你想稍微实验一下,你可以看看我们在 第六章 中展示的各种量子门的向量表示,理解 Qiskit®门库,看看你是否可以将这些门添加到 gates 字典中。

    在这个第一个例子中,我们探讨了如何将单个量子比特和门作为向量和矩阵构建,以及如何通过向量乘法运行量子比特通过门。现在让我们用两个量子比特来做同样的事情...

  5. 设置我们的多量子比特向量。

    首先,我们扩展我们的狄拉克标记的量子比特组合:,和

    这些分别代表量子比特 0,第一个量子比特 1 和第二个 0,第一个量子比特 0 和第二个 1,以及两个量子比特 1。在这里,我们使用 向后 Qiskit® 符号表示我们的量子比特,从第一个量子比特()作为向量子符号中的 最低有效位LSB)开始,如下所示:

    这些向量的表示分别是如下:

    我们已经知道如何将量子比特作为 2x1 NumPy 数组构建,所以让我们将其扩展到 4x1 向量。使用 NumPy,例如,这样创建 量子比特向量:

    np.array([1,0,0,0])
    

    在示例代码中,我们使用多量子比特数组设置了一个字典:

    twoqubits = {"|00\u27E9":np.array([1,0,0,0]),
        "|01\u27E9":np.array([0,1,0,0]),
        "|10\u27E9":np.array([0,0,1,0]),
        "|11\u27E9":np.array([0,0,0,1]),
        "|PH\u27E9":np.array([0.5,-0.5,0.5,-0.5])}
    for b in twoqubits:
      print(b, "\n", twoqubits[b]) 
    
  6. 设置我们的多量子比特门矩阵。

    双量子比特量子门由 4x4 矩阵表示,例如 受控非CX)门,如果控制第二个量子比特()设置为 1,则翻转第一个量子比特():

    这种类型的门矩阵,其中一个量子比特作为控制,另一个作为受控,根据你选择的控制量子比特不同而有所不同。如果 CX 门的方向相反,第一个量子比特()作为控制量子比特,矩阵将看起来像这样:

    这是构建门的方法:

    twogates ={"cx":np.array([[1, 0, 0, 0], [0, 1, 0, 0], 
        [0, 0, 0, 1], [0, 0, 1, 0]]), 
        "swap":np.array([[1, 0, 0, 0], [0, 0, 1, 0], 
        [0, 1, 0, 0], [0, 0, 0, 1]])}
    

    这里是一个针对 量子比特的 CX 矩阵点乘示例:

    np.dot(np.array([[1, 0, 0, 0], [0, 1, 0, 0], 
        [0, 0, 0, 1], [0, 0, 1, 0]]), np.array([0,0,0,1]))
    

    这里是示例代码:

    twogates ={"cx":np.array([[1, 0, 0, 0], [0, 1, 0, 0], 
        [0, 0, 0, 1], [0, 0, 1, 0]]), 
        "swap":np.array([[1, 0, 0, 0], [0, 0, 1, 0], 
        [0, 1, 0, 0], [0, 0, 0, 1]])}
    for g in twogates:
      print(g, "\n", twogates[g].round()) 
    print ("\n")
    
  7. 然后,我们将门应用到我们的比特上,查看结果:

    for g in twogates:
        print("Gate:",g) 
        for b in twoqubits:
            print(b,"\n",twoqubits[b],"->", 
                np.dot(twogates[g],twoqubits[b])) 
        print("\n")
    

    多量子比特矩阵操作的主要收获是输出向量与输入向量具有相同的维度;没有信息丢失。

还有更多...

量子门的一个其他方面,通常不适用于经典门,是它们是可逆的。如果你反向运行门,你将得到量子比特的输入状态,并且没有信息丢失。本章的最后食谱说明了这一点。

示例代码

以下示例的样本文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter02/ch2_r4_reversible_gates.py:

  1. 让我们先导入所有需要的:

    import numpy as np
    from math import sqrt
    
  2. 设置基本的量子比特向量和门矩阵。当打印出门时,我们比较门矩阵与其复共轭。如果它们相同,则门及其逆是相同的:

    qubits = {"|0\u232A":np.array([1,0]), 
        "|1\u232A":np.array([0,1]), 
        "(|0\u232A+|1\u232A)/\u221a2":1/sqrt(2)*np.
        array([1,1])}
    for q in qubits:
      print(q, "\n", qubits[q]) 
    print ("\n")
    gates ={"id":np.array([[1, 0], [0, 1]]),
        "x":np.array([[0, 1], [1, 0]]), 
        "y":np.array([[0, -1.j], [1.j, 0]]), 
        "z":np.array([[1, 0], [0, -1]]), 
        "h":1/sqrt(2)*np.array([[1, 1], [1, -1]]), 
        "s":np.array([[1, 0], [0, 1j]])}
    diff=""
    for g in gates:
      print(g, "\n", gates[g].round(3)) 
      if gates[g].all==np.matrix.conjugate(gates[g]).all:
          diff="(Same as original)" 
      else:
          diff="(Complex numbers conjugated)" 
      print("Inverted",g, diff, "\n",     np.matrix.conjugate(gates[g]).round(3)) 
    print ("\n")
    
  3. 通过应用门然后是其复共轭,并比较结果与输入,来证明基本量子门是可逆的。对于可逆的量子门,这将使量子比特回到起始状态:

    for g in gates:
        input("Press enter...")
        print("Gate:",g) 
        print("-------")
        for q in qubits:
            print ("\nOriginal qubit: ",q,"\n", 
                qubits[q].round(3))
            print ("Qubit after",g,"gate: \n",
                np.dot(gates[g],qubits[q]).round(3))
            print ("Qubit after inverted",g,"gate.","\n",
                np.dot(np.dot(gates[g],qubits[q]),
                np.matrix.conjugate(gates[g])).round(3))
        print("\n")
    

运行示例

当你运行这个ch2_r4_reversible_gates.py脚本时,它将执行以下操作:

  1. 如前所述,创建并打印出我们的量子比特和量子门的向量和矩阵表示。

    这次,我们添加了三个新的门:

    img/Formula_02_135.jpgimg/Formula_02_136.jpgimg/Formula_02_137.jpg

    在这里,YZ在相应的轴上执行img/Formula_02_138.png旋转,本质上在布洛赫球上的yz轴上充当 NOT 门。S 门为门添加了新的功能,即绕z轴的img/Formula_02_139.png旋转。我们将在第六章中更详细地介绍这些门,理解 Qiskit®门库

    ![Figure 2.14 – 量子门及其逆 img/Figure_2.14_B14436.jpg

    Figure 2.14 – 量子门及其逆

    复数的复共轭是通过改变其虚部的符号得到的,因此对于矩阵中只有实数的门,复共轭不起作用,门就是其自身的逆。

  2. 然后,对于我们的每个量子比特,我们应用每个门然后是其逆门,并展示我们最终得到的量子比特状态与开始时相同。

    下面的例子是关于 X 和 S 门的:

![Figure 2.15 – X 门和反转 X 门对三个量子比特状态的影响img/Figure_2.15_B14436.jpg

Figure 2.15 – X 门和反转 X 门对三个量子比特状态的影响

反转的 X 门就是它本身,将其应用于一个量子比特两次会恢复原始的量子比特状态:

![Figure 2.16 – S 门和反转 S 门()对三个量子比特状态的影响img/Figure_2.16_B14436.jpg

Figure 2.16 – S 门和反转 S 门(img/Formula_02_140.png)对三个量子比特状态的影响

S 门的逆称为img/Formula_02_141.png门,其中img/Formula_02_142.png是 S 的复共轭。应用 S 然后是img/Formula_02_143.png会恢复原始的量子比特状态。

参见

  • 《量子计算与量子信息》,艾萨克·L. 库恩;迈克尔·A. 尼尔森,剑桥大学出版社,2010 年,第 4.2 章单量子比特操作,以及 第 4.3 章控制操作

  • 《费曼物理学讲义》,费曼,理查德·P.;莱顿,罗伯特·B.;桑德斯,马修,1965 年,Addison-Wesley。查看在线版本,以及关于幅度和向量的章节,了解更多关于狄拉克符号的信息:www.feynmanlectures.caltech.edu/III_08.html#Ch8-S1.

  • 要快速查看单个量子比特的 Bloch 球表示,请查看 Qiskit Advocate James Weaver 的 grok bloch 应用程序:github.com/JavaFXpert/grok-bloch.

  • 您可以从自己的 Python 环境中安装并运行它,或者在此处在线运行:javafxpert.github.io/grok-bloch/.

  • 该应用程序支持我们迄今为止测试过的简单 X 和 H 门,以及以下章节中将要涉及的其他门,如 Y、Z、Rx、Ry、Rz 等。要深入了解 Qiskit® 提供的量子门,请参阅第六章理解 Qiskit® 门库

第三章:第三章:IBM Quantum Experience® – 量子拖放

在 2016 年初的云中发生了一件相当令人惊奇的事情;一种新型计算机向世界敞开了它的怀抱——一个可编程的量子计算机。

在本章中,我们将简要介绍 IBM Quantum Experience®的早期历史、如何到达那里以及如何开设用户账户。我们将查看用于编程 IBM 量子计算机(电路作曲家)的拖放用户界面。

此外,我们还将快速浏览一下如何通过使用底层的 OpenQASM 编码在 IBM Quantum Experience®和 Qiskit®之间来回移动。

本章我们将涵盖以下内容:

  • 介绍 IBM Quantum Experience®

  • 使用电路作曲家构建量子分数

  • 投掷量子硬币

  • 在世界之间穿梭

我们在这里不会停留太久;只需足够的时间来触及表面,展示我们将在第四章“从地面开始使用 Terra”中使用的量子电路,并感受一下可用的各种门。

技术要求

本章中我们将讨论的量子程序可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter03

如果您还没有,请为自己获取一个 IBM Quantum Experience®账户。有关信息,请参阅第一章“准备您的环境”中的创建您的 IBM Quantum Experience®账户

介绍 IBM Quantum Experience®

IBM Quantum Experience®是一个开放平台,供人们开始他们的量子计算之旅。在这里,您可以免费访问一系列 IBM 量子计算机,从单个量子位到 15 个量子位(截至写作时),以及一个在 IBM POWER9™硬件上运行的 32 量子位模拟器。这将在您的指尖提供大量力量。

IBM Quantum Experience®于 2016 年 5 月开放,这是世界上首次宣布公众现在可以访问云中的实际量子计算硬件。从那时起,其他几家公司也宣布了类似的计划,并开放了云量子计算,最初是在模拟器上。在这一群人中,值得注意的是谷歌、微软、Rigetti、Qutech 等。截至本书编写时,IBM 通过其 IBM Quantum Experience®免费提供硬件和软件量子计算,我们将专注于该平台。

从您的网络浏览器中,访问以下 URL,并使用您的 IBM Quantum Experience®账户登录:quantum-computing.ibm.com/

您现在位于 IBM Quantum Experience®的主着陆页,从这里您可以访问所有量子体验工具。

从这里,您将看到以下内容:

  • 在右侧面板中,我们有可供你使用的后端。点击每个后端都会弹出一个数据页面,显示访问状态、提供商访问、芯片结构、错误率数据、量子比特数量、基门列表等。

  • 在中心区域,你找到你的工作台。这里有最近创建的电路列表、当前正在运行的实验以及你之前的实验结果;当你第一次进入这里时,它将非常空。从这个区域,你还可以管理你的用户资料、配置通知、获取你的 API 密钥等。

  • 在左侧,我们有主要工具和帮助资源。这些将在下一节的如何做…中更详细地描述。

图 3.1 – IBM 量子体验®首页

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_3.1_B14436.jpg)

图 3.1 – IBM 量子体验®首页

现在我们已经成功登录并环顾四周,让我们来看看我们可用的量子计算编程工具。从右侧的主菜单中,你可以访问以下页面:

  • 结果

  • 电路作曲家

  • 量子实验室

图 3.2 – IBM 量子体验®编程工具

图 3.2 – IBM 量子体验®编程工具

现在我们来逐一查看这些页面。

结果

IBM 量子体验®的结果部分只是你挂起的作业和之前运行的量子计算程序的长列表。你可以通过执行时间、服务(后端)等变量进行搜索、排序和筛选:

图 3.3 – 结果页面

图 3.3 – 结果页面

IBM 量子体验®的结果面板不仅包括从电路作曲家运行的作业,还包括你使用具有相同 ID 的本地 Qiskit®在 IBM Quantum®后端运行的所有作业。

每个作业不仅包括你的作业结果,还包括其他数据,例如作业在处理每个阶段的停留时间、运行时间、作业状态、转换后的电路图以及电路的 OpenQASM 代码。

电路作曲家

电路作曲家是处理你的量子分数(即 IBM 量子体验®所说的使用电路作曲家工具构建的量子程序)的主要工具。我们将在本章的食谱中详细介绍它,但在这里我会为你快速概述其组件:

图 3.4 – 电路作曲家文件页面

图 3.4 – 电路作曲家文件页面

就像结果面板有一个作业列表一样,电路作曲家文件面板有一个你的电路列表。从这里,你可以打开和运行你使用电路作曲家创建的所有电路。

你还可以点击新建电路从头开始,这将在未命名的电路上打开电路作曲家:

图 3.5 – 电路作曲家中的空白电路

图 3.5 – 电路作曲家中的空白电路

无 Qiskit®重叠

结果 窗格不同,Circuit Composer 窗格不包含您从本地 Qiskit® 环境运行的任何电路。这里只有您在 IBM Quantum Experience® 中创建的量子分数。如果您想在这里看到您的 Qiskit® 电路,必须将它们作为 OpenQASM 代码导入。请参阅本章末尾的 在两个世界之间移动 食谱。

一旦您打开或创建了一个电路,一系列新的工具将打开以帮助您构建您的量子分数。这些将在下一食谱 使用 Circuit Composer 构建 quantum scores 中介绍。

量子实验室

第三个工具包是由 Qiskit® 团队整理的 Jupyter Notebook 教程集合。您可以从 Qiskit tutorials 磁贴访问它们。您也可以从该面板创建您的 Jupyter Notebooks,它们将像之前窗口中的电路一样显示在此窗口中:

图 3.6 – 量子实验室

图 3.6 – 量子实验室

在笔记本中运行 Python 程序

您还可以使用 Jupyter Notebook 环境运行本书中包含的量子计算 Python 示例脚本。查看 第一章 中的 下载代码示例 食谱,准备您的环境,以快速回顾。

除了您将用于编写量子程序的工具外,IBM Quantum Experience® 还包括一些扩展的帮助,形式为两个额外的页面:

  • 文档:此页面包含了一系列入门教程,Circuit Composer、算法等更广泛的说明。这是在您完成本书的学习后探索 IBM Quantum Experience® 的一个好起点。

  • ibm-q-experience) 和 Qiskit® (qiskit)。这些社交环境充满活力且反应迅速,您可以以互动的方式提出问题、分享想法等。知识渊博的成员和版主不会长时间不回答问题!

使用 Circuit Composer 构建 quantum scores

本食谱将指导您完成在 IBM Quantum Experience® 中创建量子分数的基本步骤,以了解作曲家的工作方式,如何构建和修改分数,以及最后如何使用 Inspect 功能逐步分析分数。

拖放编程

本章中的食谱将在 IBM Quantum Experience® 网络环境中运行,使用拖放界面,以直观的方式很好地可视化您正在做的事情。

如何操作...

让我们构建自己的一个小型量子分数:

  1. 从您的网络浏览器(Chrome 似乎效果最好),访问以下 URL,然后使用您的 IBM Quantum Experience® 账户登录:quantum-computing.ibm.com/

  2. 在左侧面板中,选择 Circuit Composer

    这将打开作曲家到一个空白 未命名电路

  3. 可选:设置要操作的量子比特数量。

    在默认设置中,你会看到三行,就像乐谱一样(因此得名量子代码)。每一行代表一个量子比特,基本代码是为 5 量子比特机器设计的。如你将在第五章中看到的“使用 Qiskit® 遍历 IBM Quantum® 硬件”,“比较后端”食谱,这目前是免费 IBM 量子机器最常用的设置。

    对于这个例子,我们只想使用 1 个量子比特以保持清晰。如果我们使用所有五个,显示的结果也将包括我们不会使用的四个的结果,这可能会造成混淆。

    因此,在你刚刚打开的未命名电路选项卡中,将鼠标悬停在量子比特标签(q0)上。标签会变成一个垃圾桶图标。使用这个图标移除量子比特,直到只剩下一个为止。你的量子代码现在只有一行。

    在这一行前面是标签,q0,这是你的量子比特的名称:

    图 3.7 – 空的 1 量子比特量子代码

    图 3.7 – 空的 1 量子比特量子代码

  4. 门添加到代码中。

  5. 现在将 门拖到你的代码的q0行。

    小贴士

    如你将在第四章中进一步看到,“从 Terra 的基础开始”,在 Qiskit® 中,NOT 门用 X 表示。

    你现在添加了一个 X,或者 NOT 门,它将量子比特从其初始设置值 0 翻转到 1:

    图 3.8 – 添加了 NOT 门

    图 3.8 – 添加了 NOT 门

    操作术语表

    要获取有关可用指令的更多信息,请点击电路作曲家右上角的(i)图标,并选择操作术语表以打开对所有指令(门、测量等)的详尽指南。

  6. 现在,添加一个测量指令来完成你的电路。

    如果你想运行你的代码并获得结果,则需要测量指令。它测量q0量子比特的状态,并将结果(0 或 1)写入经典寄存器(c1),这样你就可以看到实验的结果。

    在多量子比特电路中,没有必要显示所有经典寄存器作为线条。相反,它们由一个带有代表其数量的经典寄存器标签的线条表示;例如,c5代表五个经典寄存器:

    图 3.9 – 添加了测量指令

    图 3.9 – 添加了测量指令

  7. 你现在可以运行你的电路了。

    可选地,通过首先选择未命名电路并为实验起一个好名字来保存电路。

    点击在 ibmq_qasm_simulator 上运行按钮。

  8. 查看结果。

    要查看作业结果,请点击位于运行按钮正下方的作业图标。作业的结果将显示:

    图 3.10 – 作业结果框

    图 3.10 – 作业结果框

    点击作业结果框会打开结果页面,并显示你刚刚运行的作业的最终结果。在这种情况下,我们得到了一个1的结果,100%确定:

    图 3.11 – 作业结果:1,100% 确定性

    图 3.11 – 作业结果:1,100% 确定性

  9. 现在,让我们来玩一会儿。

    随意将一些量子指令拖到你的分数中,上下调整量子比特的数量。你现在正在构建复杂的量子电路,但并不一定是工作量子程序或算法。这就像在你经典计算机上随机焊接门,或者随机往锅里加食材做饭。你可能会得到某种结果,但可能没有什么有用的或可食用的。但这是很有趣的!

    这里有一个例子——看看你是否能重新创建它,然后检查它看看它做了什么(如果有):

图 3.12 – 随机拖放电路

图 3.12 – 随机拖放电路

只需看看那个电路的复杂结果。我想知道它是什么?此外,注意页面底部的两个图形框:测量概率Q-球体。我之前一直忽略了它们,但现在让我们来看看它们是什么。

更多内容

分数就像音乐乐谱一样从左到右,按时间顺序读取。这意味着乐谱左侧的门先于右侧的门执行。你可以通过使用作曲家的检查功能来检查你的电路应该如何表现:

  1. 在 IBM Quantum Experience® 中,打开你刚刚创建的电路,该电路包含一个量子比特、一个 NOT 门和一个测量指令。

  2. 从顶部菜单中选择检查

    在打开的检查器窗口中,你现在可以通过点击>来逐步查看你的分数,以了解代表你的量子比特的状态向量是如何随着你应用门而变化的。你还会看到所谓的 Q-球体,它是电路可能结果的图形表示。我们第一次在第二章中遇到了状态向量,使用 Python 进行量子计算和量子比特。更多关于 Q-球体的信息可以在第六章中找到,理解 Qiskit® 门库

    在我们的情况下,只有一个 X 门,因为我们把量子比特设置为从 0 开始,我们预计状态向量将从 图 3.11 – 作业结果:1,100% 确定性 开始,然后在我们应用第一个 X 门时变成 图 3.11 – 作业结果:1,100% 确定性

图 3.13 – 将 X 门的作用可视化为我们量子比特的状态向量以及 Q-球体

图 3.13 – 将 X 门的作用可视化为我们量子比特的状态向量以及 Q-球体

图 3.13 – 将 X 门的作用可视化为我们量子比特的状态向量以及 Q-球体

Q-sphere 指示我们将只得到一个结果,并且这个结果将是 img/Formula_03_005.png。如果你将 Statevector 显示切换到 Measurement Probabilities 选项,你可以验证我的说法,即电路确实应该每次都产生 100%的结果:

图 3.14 – 以 100%的确定性得到结果 1

图 3.14 – 以 100%的确定性得到结果 1

Inspect 工具允许你在任何门处测试你的电路。在实际量子计算机上运行时,这是不可能的,因为测试量子位就等同于测量它,然后量子位会失去其量子特性,表现得像经典比特。这里所做的是使用状态向量模拟器快速运行到那个点的电路。

如果你想在检查中包含初始 img/Formula_03_006.png 状态,那么在你的电路的第一个门之前添加一个障碍指令。障碍不会操作量子位,但会让 Inspect 工具记录量子位的初始状态:

图 3.15 – 在第一个门之前添加障碍门

图 3.15 – 在第一个门之前添加障碍门

在接下来的章节中,我们将创建一个 Inspect 功能的变体,在我们运行电路之前查看它们。更多信息,请参阅 第四章 中的 Visualizing circuits and results 菜单,从 Terra 的基础开始

抛量子硬币

我使用的例子之一可能是你可以构建的最简单有用的量子程序:模拟抛硬币。

我们将在 第四章从 Terra 的基础开始 中更详细地介绍量子抛硬币,但我会在这里使用那个程序作为例子。它非常小,而且理解起来并不复杂。

如我们简要讨论过的 第二章使用 Python 进行量子计算和量子比特,与经典计算机相比,量子计算机为你提供概率性或随机计算。在这种情况下,我们将我们的量子比特设置为一种叠加态,当我们测量量子比特时,它会产生得到 0 或 1、或正面或反面的概率。

如何做到这一点...

构建量子电路并运行它。这是我们将在本书的 Qiskit® 部分稍后回过头来讨论的电路:

  1. 登录到 IBM Quantum Experience® quantum-computing.ibm.com/composer

  2. 在 IBM Quantum Experience® 中,从左侧面板选择 Circuit Composer

  3. 创建一个新的电路。

  4. 找到你需要的大门。

    在这个菜谱中,我们将只使用两个量子指令,其中一个我们在第二章使用 Python 的量子计算和量子比特中的Hadamard 门菜谱中简要讨论过。记住,H 门将输入量子比特创建一个叠加态。

    我们还将使用测量指令来测量量子比特并将结果写入分数底部的经典比特行。

  5. 通过从作曲家的部分拖动 H 门到第一个量子比特行来构建电路。然后,将测量指令拖到同一行,放在 H 门右边。你的量子分数现在完成了。你的电路应该看起来像这样:图 3.16 – 简单的抛硬币电路

    图 3.16 – 简单的抛硬币电路

    但为什么我有这么多我没有使用的量子比特?

    作曲家默认设置的量子比特数量是3。只要你在同一个量子比特上放置测量指令,你就可以将你的 H 门放在任何你想要的量子比特上。

    没有什么阻止你在每个量子比特上放置一个 H 门,然后跟一个测量门。你那时正在构建一组五个同时抛硬币。

    你也可以通过点击位于最低量子比特直接下方的+图标,或者将鼠标悬停在量子比特上并点击出现的垃圾桶图标来更改量子比特的数量。

  6. 通过首先点击未命名电路并给实验起一个好名字,然后保存电路来保存电路。现在你的电路已经准备好运行了。

  7. 点击在 ibmq_qasm_simulator 上运行按钮。

  8. 查看结果。

    要查看作业的结果,请点击位于运行按钮正下方的作业图标。等待作业显示完成结果,然后点击作业结果框以打开结果页面。你的 1,024 次相同电路运行产生了两个可能结果 0 和 1 的统计 50/50 分布,正如我们在第二章使用 Python 的量子计算和量子比特中的快速介绍量子门菜谱中预期的那样。结果看起来可能如下:

    图 3.17 – 我们量子抛硬币的 50/50 结果,一半正面和一半反面

    图 3.17 – 我们量子抛硬币的 50/50 结果,一半正面和一半反面

    在这里,输出得到 0 或 1 的概率大致相等,就像一个物理硬币,正面和反面的概率大致相等。

  9. 在真实的量子计算机上运行你的分数。

    但实际上,在模拟器上运行你的分数可能有些令人失望。这并不是你开始量子计算之路的原因。但不用担心,我们可以做得更好。

    我们将再次运行分数,但选择非模拟后端。对于这个简单的量子分数,如果后端真的没有调好,1 量子比特抛硬币的结果将是相同的。然而,对于更复杂的量子程序,模拟器和真实量子计算机的结果可能会有所不同。

  10. 点击运行设置按钮,然后选择除ibmq_qasm_simulator之外的其他后端

    要了解每个后端可能需要等待的时间,您可以访问主IBM Quantum Experience仪表板,查看后端的各个队列。

  11. 等待结果。

    要查看作业结果,请点击位于运行按钮正下方的作业图标。等待作业显示完成结果,然后点击作业结果框以打开结果页面。

    排队

    默认情况下,您的量子分数将在模拟一个通用 32 量子比特量子计算机的在线 IBM Quantum®模拟器上运行。模拟器是测试和调整您的量子分数以确保它们按预期运行的好方法。在模拟器上运行通常非常快。然而,在真实的量子计算机上运行可能需要更长的时间,因为您将与其他用户共享这些计算机。

  12. 结果可能看起来像以下这样:

图 3.18 – 在真实量子计算机上的量子抛硬币结果

图 3.18 – 在真实量子计算机上的量子抛硬币结果

注意结果看起来与您在模拟器上得到的结果相当相似。这通常情况下是成立的,除非您运行分数的物理量子计算机量子比特不平衡,并且偏向于某一结果而不是另一个。记住,实际的量子比特是物理实体,而不是完美的数学抽象。在第五章,“使用 Qiskit®游览 IBM Quantum®硬件”,我们将探讨其他事项,例如单个量子比特的行为,以及当在它们上运行电路时可以期待什么。

还有更多

非模拟后端只能固有地运行一组基门,所有其他门都是基于这些基门构建的。当您运行量子程序时,它会被软件解释,相当复杂的高级门架构会被转换为u1u2u3idcx。结果是,您在 Qiskit®上编写的所有量子程序都可以仅使用这些门来表示。

您可以从 IBM Quantum Experience®网页控制台的您的后端部分看到量子后端使用的基门,通过点击您感兴趣的后端:

图 3.19 – ibmq_santiago 后端基门为 u1、u2、u3、cx 和 id

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_3.19_B14436.jpg)

图 3.19 – ibmq_santiago 后端基门为 u1、u2、u3、cx 和 id

这为什么重要呢?嗯,将门(在您的分数中)转换为可以在后端运行的基门的过程称为 转换,并在运行程序之前完成。转换器将您的输入分数转换为基门,然后这些基门在后端运行。

现在,结果是您所使用的常规门并不总是可以直接转换为单个基门。有时转换器必须做一些工作,通过用其他门的簇替换您的门来重构您的电路。

例如,这里是在 IBM 的 5 量子比特机器上运行的简单量子抛硬币的原始和转换版本:

图 3.20 – 原始和转换后的量子抛硬币

图 3.20 – 原始和转换后的量子抛硬币

如您所见,变化不大。H 门现在是一个 U2 门,输入为 0 和 img/Formula_03_007.png,我们的简化 3 量子比特分数被后端实际五个量子比特所取代。但电路的深度保持不变 – 两个门长。

对于更复杂的电路,事情变得更加复杂,主要是因为其中包含其他门。在下面的例子中,它来自 第九章Grover 搜索算法,除了 X 和 H 门之外,还有更复杂的门,例如具有两个输入和一个输出的 受控-受控非CCX)门。原始分数的深度为 22 门:

图 3.21 – 三量子比特的原始 Grover 搜索算法

图 3.21 – 三量子比特的原始 Grover 搜索算法

因为量子计算机后端不能直接使用 X、H 和 CCX 门,所以这些门必须转换为 U 和 CX 门。转换后的分数深度为 49 门:

图 3.22 – 转换后的三量子比特 Grover 搜索算法

图 3.22 – 转换后的三量子比特 Grover 搜索算法

障碍的重要性

如果您仔细观察原始的 Grover 电路,您会注意到在某些位置,您会看到两个相同类型的门紧挨着。记得从 第二章使用 Python 的量子计算和量子比特,量子门是可逆的,并且两个连续的相同门可能会简单地相互抵消。如果我们让转换器工作,它就会移除重复的门以简化电路。这通常不是最好的解决方案,因为这些门可能是需要保持完整的大结构的一部分。

这就是障碍组件和分数中的垂直灰色栏发挥作用的地方。一个障碍告诉编译器不要跨过它;如果两个相同的门由一个障碍隔开,编译器不会删除它们,而是将每个门转换为正确的门类型。看看 Grover 分数的编译器版本,你就会明白我的意思。

在世界之间移动

现在你已经看到了如何在电路作曲家中创建你的量子分数,以及如何在模拟器和真实的 IBM 量子计算机上运行它们。但本书的其余部分将关于如何在 Qiskit®中使用你的程序进行工作。那么我们就此告别 IBM Quantum Experience®吗?

还不快呢。IBM Quantum Experience®是一个很好的环境,用于学习如何构建量子分数,你不必调试 Python 代码或担心你的环境是否更新(IBM 为你处理这些),实际上,将你在 IBM Quantum Experience®中创建的内容移动到 Qiskit®非常简单。

你有两个选项:

  • Qiskit®:使用 Qiskit®代码导出,你的量子分数被转换为可以直接粘贴到 Python 解释器中并运行的 Python 代码。这是从 IBM Quantum Experience®到 Qiskit®的单程之旅。

  • 使用 Qiskit®中的QuantumCircuit.from_qasm_str()方法导入该代码。要反向操作,使用<circuit>.qasm()从 Qiskit®导出你的电路,然后将其粘贴到代码编辑器中以进行反向操作。

准备工作

该食谱的 Python 文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter03

如何做到这一点...

让我们先从导入你的抛硬币实验的 QASM 代码开始:

  1. 从你的网络浏览器中,访问以下 URL,然后使用你的 IBM Quantum Experience®账户登录:quantum-computing.ibm.com/

  2. 选择电路作曲家,在导航栏中点击电路

  3. 电路作曲家文件页面上,点击你的抛硬币电路。

  4. 电路作曲家窗口中,在左侧面板中选择<>代码编辑器。

  5. 要将你的量子分数导出为 Qiskit®代码,在下拉菜单中选择print(circuit)行并运行代码。这将产生类似以下输出的结果:图 3.23 – 导出的抛硬币 Python 代码及其输出

    图 3.23 – 导出的抛硬币 Python 代码及其输出

  6. 要将你的量子分数导出为 QASM 代码,在下拉菜单中选择.qasm文件,然后将其导入到 Qiskit®中。

  7. 现在,转到你的 Qiskit®环境,并运行ch3_r1_import_qasm.py文件。如果你忘记了如何到达那里,请查看第一章准备你的环境中的打开配方文件配方。

  8. 对于这个简单的食谱,我们只需要 QuantumCircuit 方法,所以我们将添加它:

    from qiskit import QuantumCircuit
    
  9. 首先,我们从 IBM Quantum Experience® 导入 QASM 字符串,无论是作为粘贴的字符串还是作为保存的文件:

    qasm_string=input("Paste in a QASM string from IBM     Quantum Experience (or enter the full path and file     name of a .qasm file to import):\n")
    if qasm_string[-5:] == ".qasm":
        circ=QuantumCircuit.from_qasm_file(qasm_string)
    else:
        circ=QuantumCircuit.from_qasm_str(qasm_string)
    
  10. 如果你将 QASM 代码粘贴到提示符中,你最终会得到以下示例类似的内容。如果你输入文件名,文件将被导入,结果大致相同:

    Ch 3: Moving between worlds 1
    -----------------------------
    
  11. 从 IBM Qx 粘贴一个 QASM 字符串(或输入完整路径和文件名以导入 .qasm 文件):

    OPENQASM 2.0;
    include "qelib1.inc";
    qreg q[1];
    creg c[1];
    h q[0];
    measure q[0] -> c[0];
    
  12. Enter 键,电路将被导入到 Qiskit®,现在可以用于量子计算。最后的 print(circuit) 应该显示以下内容:![图 3.24 – 导入的抛硬币量子电路 图片

    图 3.24 – 导入的抛硬币量子电路

  13. 你现在已经将你的 QASM 代码导入到 Qiskit® 并创建了一个名为 circ 的量子电路对象。

现在让我们尝试从 Qiskit® 创建和导出 OpenQASM 代码:

但我还没有学会如何用 Qiskit® 编码

到目前为止,你还没有在 Qiskit® 中创建任何量子电路。不用担心,我们将使用 random_circuit() 方法创建随机电路,你可以导出并导入到 IBM Quantum Experience® 中查看。

  1. 在你的 Qiskit® 环境中,打开 ch3_r2_export_qasm.py 文件。

  2. 导入的随机电路方法如下:

    from qiskit.circuit.random.utils import random_circuit
    
  3. 首先,我们创建并打印一个随机量子电路:

    circ=random_circuit(2,2,measure=True)
    

    在这个例子中,随机电路的深度设置为 2,这意味着我们将创建一个最多包含两个门的电路。量子比特的数量也设置为 2,结果显而易见。你可以调整这些数字来查看会出现什么。

    电路可能看起来像这样:

    ![Figure 3.25 – 随机创建的电路 图片

    图 3.25 – 随机创建的电路

  4. 接下来,我们将电路导出为 QASM 代码:

    circ.qasm(formatted=True, filename="Circuit.qasm")
    

    QASM 代码被显示,并且也被保存到你的本地环境中,文件名由你指定:

    OPENQASM 2.0;
    include "qelib1.inc";
    qreg q[2];
    creg c[2];
    id q[0];
    u1(3.072089) q[1];
    rzz(0.7638327) q[1],q[0];measure q[0] -> c[0];
    measure q[1] -> c[1];
    
  5. 回到 IBM Quantum Experience®,你现在可以进入你刚刚创建的 Circuit.qasm 文件中的电路编辑器。

  6. 观察你的电路立即出现在电路作曲家:![Figure 3.26 – 导入的随机生成电路 图片

    图 3.26 – 导入的随机生成电路

  7. 你现在可以运行电路,将新门拖放到电路中,或者只是移动或调整现有门以查看你的更改如何使电路表现不同。别忘了使用 Inspect 功能、测量概率Q-sphere 显示来逐步查看你的电路并了解其工作原理。

它是如何工作的...

当你使用 circ=QuantumCircuit.from_qasm_file() 方法导入你的量子分数时,你分配给它的 circ 对象现在是一个 QuantumCircuit() 对象,这与你在后续章节中创建量子电路的方式非常相似。

使用 Qiskit®,你可以显示电路,向电路中添加门,等等。

在本章中,我们将不会对电路进行任何复杂的操作,但在此处做一个书签,以便你在开始创建自己的电路时回到第四章,从“地面开始”使用 Terra。

更多内容

当你阅读这本书时,你将主要在你的 Python Qiskit®环境中运行实验。如果你想,你可以将你在 IBM Quantum Experience®环境中构建的电路拉过来,在这里也运行它们。也许你更喜欢图形拖放环境,并且可以实时编辑你的分数?

在接下来的 Qiskit® Python 示例中,我们将创建名为qccirccircuit等命名的QuantumCircuit对象。你可以将这些导出为 QASM,然后使用circ.qasm(formatted=True, filename="Circuit.qasm")命令将它们导入到 IBM Quantum Experience®。

如果你想,你可以使用print(circ)circ.draw()函数来打印电路,然后在作曲家手动重新创建它。

我们从使用 IBM Quantum Experience®构建量子分数的示例代码

如果你记得,我们在本章第二个菜谱中展示了一个相当复杂但随机的电路。以下是该电路的 QASM 代码。你可以导入它并查看是否得到相同的结果:

OPENQASM 2.0;
include "qelib1.inc";
qreg q[5];
creg c[5];
h q[0];
y q[1];
y q[2];
ch q[3],q[4];
ccx q[2],q[0],q[1];
u3(2.1128893,1.9882648,5.5897911) q[3];
tdg q[4];
cu1(3.0287577) q[1],q[4];
cu3(5.1184948,2.0719417,1.8609727) q[3],q[0];
s q[4];
z q[0];
ry(3.6419028) q[1];
rz(0.402055) q[2];
cu3(1.5290482,3.844241,4.4343541) q[2],q[3];
ccx q[4],q[0],q[2];
ch q[1],q[3];
measure q[0] -> c[0];
measure q[1] -> c[1];
measure q[2] -> c[2];
measure q[3] -> c[3];
measure q[4] -> c[4];

参考资料还有

关于 OpenQASM 的更多信息,请参阅以下出版物:

第四章:第四章:从 Terra 的起点开始

在我们对 IBM Quantum Experience®作曲家和 IBM 的 Qiskit®进行简要探索,并安装 IBM 的 Qiskit®之后,我们现在准备好开始使用 Qiskit®编写量子程序。我们将放弃用户界面,将我们的量子计算之旅扩展到 Python——这是世界上最受欢迎和广泛使用的科学编程语言之一。

本章涵盖了使用 Qiskit®构建量子电路的基本要求。我们将逐步介绍几个最小化量子程序的制作,这些程序在本地 Qiskit Aer 模拟器上运行,并以数值和图表形式显示程序的结果。然后我们将迈出一步,在实际的 IBM Quantum®硬件上运行这些程序。

本章的主题是量子抛硬币,基于我们在 IBM Quantum Experience®中构建的原始量子程序。这些可能是你可以编写的最简单且具有意义的量子程序,因为它们从一些基本的量子门开始,同时也展示了概率量子计算与确定性经典计算之间的差异。

我们还将通过添加更多门来扩展最小化程序,多次运行程序以收集关于结果的数据,等等。本章我们将学习的内容将从下一章开始应用。

在本章中,我们将介绍以下食谱:

  • 构建一个 Qiskit®量子程序

  • 再次探讨量子抛硬币

  • 获取一些统计数据——连续抛掷许多硬币

  • 实现倒立抛硬币

  • 同时抛掷两个硬币

  • 抛硬币时的量子作弊?——介绍贝尔态

  • 更多量子作弊的方法——调整概率

  • 添加更多硬币——直接和作弊

  • 抛掷一些真实的硬币

技术要求

本章中讨论的量子程序可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter04

你可以在作为第一章,“准备你的环境”一部分设置的本地 Qiskit®环境中运行本章的食谱,或者你可以在 IBM Quantum Experience®的笔记本环境中运行它们。

如果你在本地环境中运行它们,我建议使用 Anaconda 安装中的内置 Spyder iPython 编辑器。这是本书中构建和运行示例所使用的编辑器。

在本书的代码示例中,你有时会看到以下这一行代码:

from IPython.core.display import display

根据你的环境,iPython 可能不会直接在输出中显示图形输出。如果是这种情况,你可以使用display()方法强制输出,如下所示:

display(qc.draw('mpl'))

此示例将量子电路qc打印到 iPython 控制台。

构建 Qiskit®量子程序

通常来说,使用 Qiskit®创建量子程序只需要几个必要的构建块。首先,您必须设置所需的基础设施并创建量子电路(在 IBM Quantum Experience®中我们称之为量子评分)。然后,您必须配置一个后端来运行您的量子程序,最后执行并检索计算结果。

以下部分是构成量子程序的 Python 构建块的摘要。

列出所需的类、模块和函数

Qiskit®包含大量的 Python 类,但对我们最初的探索来说,我们只需要基本的一些。这些用于配置以下每个组件:

  • QuantumCircuit: 这用于创建您将要执行的电路——程序。您将向电路添加门和其他组件。

  • QuantumRegister: 这代表您可以用它来构建量子程序的可用的量子比特。

  • ClassicalRegister: 这代表用于存储您的量子程序输出的经典比特。

  • Aer: 这是 Qiskit®模拟层,我们将在第七章中更详细地讨论,使用 Aer 模拟量子计算机

  • IBMQ: 此模块是执行您在 IBM Q 硬件上量子程序所必需的。它包括您与 IBM Q 交互所需的工具。

  • execute: 此组件允许您通过提供电路、后端和射击次数来运行您的程序。

使用量子寄存器和经典寄存器

为了能够构建您的量子程序,您首先需要决定您想要使用多少量子比特,以及您想要包含多少经典比特来存储您的输出。您可以明确设置这些,或者使用QuantumCircuit类自动创建寄存器。

寄存器构成了您的量子系统中的两套信息携带比特:

  • 一组量子寄存器来存储您的量子比特

  • 一组经典寄存器来存储您的常规比特

您将使用测量门来读取您的量子比特,然后将结果经典比特写入经典寄存器。

对于本书中的大多数食谱,量子比特和经典寄存器的数量将是相同的,但这不是必需的。

理解您的量子电路

您创建的量子电路实例将包含量子比特和经典比特。您将通过添加门来操作每个实例。

量子程序可以通过组合多个量子电路来组装。例如,您可以创建一个包含量子门的电路和一个包含测量门的电路。然后,您可以添加这些电路以创建一个主量子电路,该电路构成了您的量子程序。

选择运行的后端

为了能够执行你的量子程序,你必须定义一个后端。后端可以是一个本地模拟器,云中的 IBM Quantum®模拟器,或者通过云访问的实际 IBM Quantum®硬件。

初始时,我们将使用 Qiskit Aer 附带的自带的qasm_simulator后端,但我们也会在我们的量子程序上运行一些免费可用的 IBM Quantum®后端。

将你的电路作为工作运行

你可以通过提供电路、后端和射击次数来作为一个工作运行量子程序。如果你在 IBM Quantum®硬件上运行量子程序,你还可以包括一个工作监控器来跟踪你在队列中的位置。

接收你的工作结果

当你的工作运行完成后,结果将被返回。在这些初始菜谱中,当我们使用qasm_simulator后端或 IBM Quantum®硬件时,返回的结果将是 Python 字典。

对于 1 比特电路,结果可能看起来像这样:

{'1': 1} 

在这种情况下,一次射击的返回结果是处于![img/Formula_04_001.png]状态的量子比特。

结果也可能看起来像这样:

{'0': 495, '1': 505}

这里,返回的结果是 1,000 次射击,其中 495 次射击导致量子比特处于![img/Formula_04_002.png]状态,505 次射击导致![img/Formula_04_001.png]状态。

返回的结果可能更复杂。以下是一个可能的结果,这是一个运行了 1,000 次的 3 比特量子程序:

{'100': 113, '111': 139, '001': 112, '101': 114, '010': 121, '011': 133, '000': 134, '110': 134}

这里,结果出现了 113 次![img/Formula_04_004.png],出现了 139 次![img/Formula_04_005.png],等等。

但是,话不多说,让我们编写一些量子程序。

量子抛硬币再探

在这个菜谱中,我们将更详细地研究我们在 IBM Quantum Experience®中创建的第一个量子程序——量子抛硬币。再次强调,这可能是最简单的量子程序,但仍提供了真正的量子计算价值。它展示了量子计算的随机性质。为了复习,请参阅第三章**,IBM Quantum Experience® – Quantum Drag and Drop

在 IBM Quantum Experience®中,抛硬币程序看起来像这样:

![图 4.1 – IBM Quantum Experience® composer 中的简单抛硬币电路

![img/Figure_4.1_B14436.jpg]

图 4.1 – IBM Quantum Experience® composer 中的简单抛硬币电路

在量子抛硬币中,我们再次使用Hadamard 门来创建量子叠加,并使用测量门迫使叠加坍缩为两个量子比特状态之一![img/Formula_04_002.png]或![img/Formula_04_001.png],分别代表正面反面。然而,这次我们将使用 Qiskit®在 Python 中创建电路,这意味着我们还需要通过定义和创建量子电路和经典电路来定义和创建门和测量的框架。

这是一个模拟单个量子比特在叠加中概率性质的量子电路。1 量子比特电路将量子比特初始化在基态 – – 然后使用 Hadamard 门将量子比特置于叠加状态。

在我们的计算中,量子比特的状态向量看起来是这样的:

图 4.2 – 量子比特状态向量的公式

图 4.2 – 量子比特状态向量的公式

您也可以将其写成向量形式:

图 4.3 – 向量形式下的量子比特状态向量

图 4.3 – 向量形式下的量子比特状态向量

另一种向量形式是您将在这些示例中看到的 Qiskit®状态向量形式:

[0.70710678+0.j 0.70710678+0.j]

测量量子比特会导致它以大约 50%的概率坍缩到状态 ,即抛硬币。结果以数字读数、条形图和 Bloch 球的形式显示。

准备工作

本食谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r1_coin_toss.py

为了快速回顾如何运行示例程序,请参阅第一章下载代码示例食谱,准备您的环境

您现在可以编写您的第一个量子程序了。

如何做…

以下步骤在很大程度上将在本书中重复,基于基本所需的类和步骤进行基本的 Qiskit®管理。因此,请期待接下来的如何做…部分的要点将变得更加简短。

然而,具体差异将取决于您计划运行的程序以及您将使用的 Qiskit®组件。现在让我们开始程序:

  1. 导入所需的 Qiskit®类。

    首先,我们导入创建寄存器和电路、设置后端等所需的 Python 类。请参阅列出所需的 Qiskit®类、模块和函数部分的构建 Qiskit®量子程序食谱:

    from qiskit import QuantumRegister, ClassicalRegister
    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    

    此外,我们还从IPython.core.display导入display方法。这用于在我们的 Anaconda Spyder IPython 环境中正确显示图形输出,并且可能不在您的环境中需要。

  2. 创建所需的寄存器和量子电路。

    我们创建两个寄存器,一个用于量子比特,一个用于经典比特。我们还创建了一个由量子寄存器和经典寄存器组成的量子电路。量子寄存器被初始化在基态, ,而经典寄存器被设置为 0:

    q = QuantumRegister(1)
    c = ClassicalRegister(1)
    qc = QuantumCircuit(q, c)
    
  3. 向量子电路添加门。

    为了让我们的量子程序真正做些事情,我们现在添加一个哈达玛门,将量子比特置于叠加态,并添加一个测量门,我们将在程序结束时使用它来读取量子比特的值。哈达玛门是基础门之一。我们将在第六章《理解 Qiskit®门库》中更详细地研究该门以及其他门:

    qc.h(q[0])
    qc.measure(q, c)
    display(qc.draw('mpl'))
    

    通过使用qc.draw('mpl')可视化电路,我们看到,就像在 IBM Quantum Experience® composer 中一样,我们有一个包含 1 个量子比特和 1 个经典比特的量子电路,量子比特上有一个哈达玛门,以及一个将量子比特q0_0()的状态写入经典比特c0_0作为 0 或 1 的测量门:

    ![Figure 4.4 – 简单量子抛硬币电路 Figure 4.4 – 简单量子抛硬币电路

    图 4.4 – 一个简单的量子抛硬币电路

    在一个纯文本的 Python 环境中,你也可以使用print(qc)命令或使用qc.draw('text')来打印你的电路,这两个都会产生 ASCII 文本输出:

    Figure 4.5 – 以 ASCII 文本显示的电路

    图 4.5 – 以 ASCII 文本显示的电路

  4. 设置运行的后端。

    对于这个初始配方,我们将使用内置的qasm_simulator后端。我们创建一个后端变量并调用 Aer 组件来获取所需的后端信息:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 运行作业。

    为电路和所选后端创建一个量子作业,只运行一次以模拟抛硬币。然后我们运行作业并显示返回的结果;要么是表示正面,要么是表示反面。结果以 Python 字典的形式返回:

    job = execute(qc, backend, shots=1)
    result = job.result()
    counts = result.get_counts(qc)
    
  6. 打印并可视化结果:

    print(counts)
    display(plot_histogram(counts))
    

    首先,我们打印出结果:

    {'0': 1}
    

    然后,我们以直方图的形式打印出结果:

Figure 4.6 – 作为直方图的单个量子“抛硬币”结果

图 4.6 – 作为直方图的单个量子“抛硬币”结果

成功!你第一个量子抛硬币程序返回了反面,或结果。

它是如何工作的...

我们抛了一个量子硬币,它最初是向上(),在量子空间中以的叠加态旋转,当我们测量它时最终落在反面位置。

那么,发生了什么?

让我们重申:

  1. 我们创建了一个量子比特并将其初始化在基态,

  2. 然后,我们对量子比特应用了一个哈达玛门,将状态向量从布洛赫球的极点移动到赤道。

    从数学上讲,当你将 Hadamard 门应用于量子比特时,你让它经历两次旋转,首先是在y轴上的旋转,然后是在x轴上的旋转。量子比特现在处于的叠加状态,位于两个极点之间。

    想了解更多关于 Hadamard 门的信息,请参阅第六章中的使用 H 门创建叠加配方,理解 Qiskit®门库

  3. 最后,我们测量了量子比特。

    通过测量,我们破坏了叠加,实际上迫使自然界做出决定,量子比特将处于

现在,先运行你的程序几次,注意观察你是否得到正面()或反面()。如果我们做得正确,你应该能非常接近地模拟抛硬币。

获取一些统计数据——连续抛很多次硬币

好的,到目前为止,我们一次只抛一个硬币,就像现实生活中一样。

但量子计算的力量来自于多次运行相同的初始条件下的量子程序,让量子比特的叠加发挥其量子力学优势,并对大量运行进行统计分析。

使用这个配方,我们将在一瞬间进行 1,000 次抛硬币,并查看结果,看看硬币的好坏。这个硬币是否能够公平地开始一场棒球比赛?让我们看看它是如何工作的。

准备工作

这个配方的示例代码可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r2_coin_tosses.py

在这个配方中,我们将探索和扩展shots作业参数。这个参数让你控制量子作业周期(准备、运行、测量)运行的次数。到目前为止,我们已经将量子比特准备在状态,将后端设置为模拟器,然后运行一次,这代表了一个完整的周期。

在 IBM Quantum Experience®作曲家的示例中,我们运行了 1,024 次评分,这是默认值。我们发现输出变成了统计性的。在这个配方中,我们将尝试不同的射击次数,看看结果如何从统计上变化。

通常来说,你想要增加射击次数以提高统计精度。

如何操作...

与前面的配方类似,以下步骤都是你所需要的:

代码重用

对于懒惰的程序员来说,在这种情况下,正如你可能已经意识到的,电路中唯一真正的变化出现在第 6 步,在那里我们设置了射击次数。请随意重用之前的配方。

  1. 导入所需的 Qiskit® 类和方法:

    from qiskit import QuantumRegister, ClassicalRegister
    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    
  2. 创建所需的寄存器和量子电路。使用寄存器设置我们的量子电路,一个量子比特和一个经典比特。根据寄存器创建量子电路:

    q = QuantumRegister(1)
    c = ClassicalRegister(1)
    qc = QuantumCircuit(q, c)
    
  3. 向量子电路添加门。添加 Hadamard 和测量门:

    qc.h(q[0])
    qc.measure(q, c)
    display(qc.draw('mpl'))
    

    图 4.7 – 相同的简单量子抛硬币电路

    图 4.7 – 相同的简单量子抛硬币电路

  4. 将后端设置为我们的本地模拟器以运行:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 运行作业。

    注意,这次我们运行了1,000 次射击。这意味着我们运行了我们的量子电路 1,000 次,收集了结果,并提供了平均输出作为我们的结果:

    job = execute(qc, backend, shots=1000)
    result = job.result()
    counts = result.get_counts(qc)
    
  6. 打印并可视化结果:

    print(counts)
    display(plot_histogram(counts))
    

    这次输出看起来不同:

    {'0': 480, '1': 520}
    

    这里是输出的直方图形式:

图 4.8 – 1,000 次射击的结果,0 和 1 的分布大致均匀

图 4.8 – 1,000 次射击的结果,0 和 1 的分布大致均匀

它是如何工作的...

这次我们使用了底层的 Qiskit® 分析来存储和处理每次射击的结果,并将其作为连接的 Python 字典返回。最终结果是所有实际输出的统计视图。对于抛硬币,如果你运行足够多次,你预计会以大致相等的概率得到正面和反面。

尝试将射击次数调整为 10,000 或 20,000,并查看你的量子比特在统计上的行为。

在一个更复杂的量子程序中,结果通常将表明对特定结果的偏好,即量子比特统计的特定组合,其中某些解决方案被放大并比其他解决方案出现得更频繁。这是编写良好的量子算法的关键技巧之一,理解如何让电路指向正确的答案。

我们将在稍后对此进行更详细的介绍,从第九章中的探索量子相位回弹配方开始,Grover 搜索算法

更多内容...

通常来说,你通常对获取量子程序的统计结果感兴趣。在这种情况下,我们正在查看量子抛硬币的概率性质——一个随机数生成器。但有时,看到每一步的具体情况可能也很有趣。

你可以通过在运行你的量子程序时设置memory=True参数来实现这一点:

job = execute(circuit, backend, shots=10, memory=True)

此设置使量子作业保存每次射击的个别结果,你可以稍后通过使用result.get_memory()命令检索这些结果。对于 1 量子比特电路的 10 次射击,内存结果可能看起来像这样:

['1', '1', '0', '0', '1', '1', '0', '1', '0', '0']

对于一个 2 量子比特电路的 10 次射击(我们将在后面的配方中这样做),内存结果可能看起来像这样:

['10', '10', '10', '11', '11', '00', '10', '10', '01', '00']

您可以从这个列表中挖掘出任何您想要的数据,并根据需要使用其他 Python 工具进一步处理。

实现倒立硬币抛掷

在这个菜谱中,我们将对我们的第一个量子程序进行一点调整,但仍然保持相对简单。实际的硬币可以抛掷,开始时正面朝上或反面朝上。让我们再进行一次量子硬币抛掷,但起点不同,硬币朝反面。在狄拉克符号中,我们开始时将量子比特置于![img/Formula_04_001.png],而不是![img/Formula_04_002.png]。

准备中

这个菜谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r3_coin_toss_tails.py.

就像之前的菜谱一样,这个菜谱几乎与第一个硬币抛掷菜谱相同。您可以自由地重用您已经创建的内容。唯一的真正区别是,我们添加了一个新的量子门,即X(或NOT)门。

如何操作...

以下步骤在很大程度上与量子硬币抛掷重访菜谱中的步骤相同。然而,根据您创建的程序以及您使用的 Qiskit®组件,可能会有所不同。我将详细描述这些。

按照之前的示例设置您的代码,然后添加一个 X 门来翻转量子比特:

  1. 导入所需的 Qiskit®类和方法:

    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.visualization import plot_histogram
    from IPython.core.display import display
    

    注意,我们在这里没有像之前那样导入QuantumRegisterClassicalRegister方法。在这个菜谱中,我们将探讨创建量子电路的另一种不同方式。

  2. 创建包含 1 个量子比特和 1 个经典比特的量子电路:

    qc = QuantumCircuit(1, 1)
    

    在这里,我们隐式地让QuantumCircuit方法在后台创建量子比特和经典比特寄存器;我们不需要显式地创建它们。我们将使用数字和列表来引用这些寄存器。

  3. 将 Hadamard 门、X 门和测量门添加到电路中:

    qc.x(0)
    qc.h(0)
    qc.measure(0, 0)
    

    因此,这里是我们使用仅数字来引用量子比特的第一个例子。我们将 X 门添加到第一个量子比特,这里称为 0,因为 Python 从 0 开始编号,而不是从 1 开始:

    ![Figure 4.9 – The upside-down quantum coin toss circuit

    ![img/Figure_4.9_B14436.jpg]

    图 4.9 – 倒立量子硬币抛掷电路

  4. 将后端设置为我们的本地模拟器:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 要运行作业,这次,我们回到 1 次抛掷,只进行一次硬币抛掷:

    counts = execute(qc, backend, shots=1).result().    get_counts(qc)
    

    注意这里我们稍微简化了代码,并将所有执行放在一行中,因为我们只对最终的计数感兴趣。

  6. 可视化结果:

    display(plot_histogram(counts))
    

    输出看起来如下:

Figure 4.11 – 使用 initialize 方法将量子比特设置为

图 4.10 – 单次倒立量子硬币抛掷的结果

它是如何工作的...

在这个菜谱中,我们引入了一个新的门,即 Pauli-X 门。X 门在经典计算中就像 NOT 门一样工作——它翻转量子位的值。

如果量子位是 ,则它翻转成 ,如果它在 ,则它翻转成

对于像这样简单的情况,直观上很容易理解结果会是什么,但对于更复杂的量子位配置,其中 Bloch 向量指向 Bloch 球上的任意点,事情就变得有点棘手。本质上发生的事情是量子位绕 x 轴旋转了 弧度。

还有更多...

X 门在这里很有用,但还有另一种方法可以设置你的硬币投掷反面。你可以在运行电路之前使用 Qiskit Aer 的 initialize 方法将量子位的初始状态设置为

以下是如何做到这一点的示例:

  1. 创建一个与激发态对应的 Python 向量,并初始化量子电路:

    initial_vector = [0.+0.j, 1.+0.j]qc.initialize(initial_vector, 0)
    
  2. 在你的电路中找到以下代码,并将其替换为前面的 initialize 代码片段:

    qc.x(0)
    
  3. 现在正常运行程序。结果应该相似,但你的电路打印输出将如下所示:

![图 4.11 – 使用 initialize 方法设置你的量子位为 ]

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_4.11_B14436.jpg)

图 4.11 – 使用 initialize 方法设置你的量子位为

这样,你可以将任意数量的量子位初始化为任何你选择的状态。初始化向量需要满足两个主要标准:

  1. 它必须包括 2^n 可能状态中的每个状态的条目。在前面的例子中,对于 1 个量子位,我们只需要一个长度为 2 的向量。对于 2 个量子位,向量必须长度为 4,依此类推。

  2. 向量分量绝对值的平方和必须等于 1。以下是初始化量子位为 的前一个示例中的初始向量:

    initial_vector = [0.+0.j, 1.+0.j]
    

    这可以转化为以下:

图 4.12 – 初始向量

图 4.12 – 初始向量

这反过来又给出了概率之和:

图 4.13 – 概率之和

图 4.13 – 概率之和

同时掷两枚硬币

到目前为止,我们一次只玩一枚硬币,但没有任何阻止我们添加更多硬币。在这个菜谱中,我们将向模拟中添加一枚硬币,并同时掷两枚硬币。我们将通过添加第二个量子位来实现这一点,扩展量子位的数量——两倍于所有东西。

准备工作

该食谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r4_two_coin_toss.py.

如何做到这一点...

按照前面的例子设置你的代码,但使用 2 量子比特量子电路:

  1. 导入我们需要的类和方法:

    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    
  2. 使用两个量子比特和两个经典比特设置我们的量子电路:

    qc = QuantumCircuit(2, 2)
    
  3. 将 Hadamard 门和测量门添加到电路中:

    qc.h([0,1])
    qc.measure([0,1],[0,1])
    display(qc.draw('mpl'))
    

    注意我们现在是如何使用列表来引用多个量子比特和多个比特的。例如,我们通过使用[0,1]作为输入来对量子比特 0 和 1 应用 Hadamard 门:

    图 4.14 – 2 量子比特量子硬币抛掷电路

    图 4.14 – 2 量子比特量子硬币抛掷电路

  4. 将后端设置为我们的本地模拟器:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 运行一次射击的作业:

    counts = execute(qc, backend, shots=1).result().    get_counts(qc)
    

    再次,我们使用简化的代码,因为我们只对计数感兴趣。

  6. 可视化结果:

    display(plot_histogram(counts))
    

    直方图看起来像这样:

图 4.15 – 双量子硬币抛掷的结果

图 4.15 – 双量子硬币抛掷的![img/Formula_04_041.png]结果

它是如何工作的...

当你创建量子电路时,你可以设置量子比特的数量。

当你运行程序时,两个量子比特都提供了 Hadamard 门,创建了两个处于叠加态的并行量子比特。

在 Qiskit®中,量子比特编号从 0 开始,第一个量子比特,然后向上计数。一个 3 量子比特电路将包含以下量子比特,分别称为第一个、第二个和第三个量子比特:

  • 如果你使用QuantumRegister表示法,就像在量子硬币抛掷重访食谱中一样:q[0]q[1],和q[2]

  • 如果你使用列表表示法:[0,1,2]

还有更多...

在这里进行一些实验,通过向你的电路中添加更多量子比特。你会发现你可以使用这种方法来创建任何类型的随机数。输出将是一个与你的电路中量子比特数量相同的二进制数。

例如,创建一个包含 20 个量子比特的电路,并运行一次,可能会得到以下输出:

{'00101011101110011011': 1}

这可以转换为以下十进制数字:

179099

因此,你可以使用量子程序来创建任何类型的随机数。例如,你也可以使用这个设置来创建不同大小的骰子,遵循 2^n 规则(其中 n 是量子比特的数量)。所以,计算是这样的:

  • 一个量子比特 = 两种可能状态 = 硬币

  • 两个量子比特 = 四种可能状态 = 四面骰子

  • 三个量子比特 = 八种可能状态 = 八面骰子

抛硬币的量子作弊?介绍贝尔态

现在,你有了抛一个或多个量子硬币并获得概率结果的能力。这很好,我们可以想象自己使用这个新工具进行赌博,对抛硬币的结果下注。但是,由于结果是 50/50,除非我们调整赔率(也就是说,我们作弊),否则赚钱的可能性有限。

那么,在抛硬币时如何作弊呢?嗯,事先知道结果将是一个聪明的办法。结果证明,这可以通过一种称为纠缠的量子现象来实现。

通过纠缠两个量子比特,我们将它们以某种方式连接起来,使得它们不能再单独描述。在最基本的意义上,如果你有两个纠缠的量子比特,并测量其中一个为 ,那么测量另一个的结果将是

那么,我们如何利用这个来在抛硬币时作弊呢?嗯,我们创建两个量子比特,将它们纠缠在一起,然后分开它们(实际上,这在物理上做起来很棘手,但我们现在将忽略这一点)。你将一个量子比特带入赌场,而你的朋友将另一个量子比特留在房间外。

当是时候抛硬币时,你运行你的量子电路,纠缠量子比特,然后你的朋友测量他们留在房间外的量子比特。然后他们通过某种方式(例如蓝牙耳机、信号或心灵感应)偷偷告诉你他们的测量结果,。你将立即知道你的量子比特是什么,在你测量它之前,并可以据此下注。测量后,你会发现你确实猜对了,并可以领取你的奖金。

那么,如何进行量子编程呢?我们将介绍一个新的门,受控非门CX)。

准备工作

该菜谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r5_two_coin_toss_bell.py

如何做...

按照前面的示例设置你的代码,使用 2 个量子比特和经典比特:

  1. 导入我们需要的类和方法:

    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    
  2. 使用两个量子比特和两个经典比特设置我们的量子电路:

    qc = QuantumCircuit(2, 2)
    
  3. 将哈达玛门、受控非门和测量门添加到电路中。

    对于我们进行的每个电路操作,例如添加一个门,我们需要指明要对哪个量子比特进行操作。例如,要在第一个量子比特上添加一个哈达玛门,你会使用代码 qc.h(0)

    qc.h(0)
    qc.cx(0,1)
    qc.measure([0,1],[0,1])
    display(qc.draw('mpl'))
    

    上述代码给出了以下输出:

    图 4.16 – 带有受控非门以纠缠量子比特的 2 量子比特量子电路

    图 4.16 – 带有受控非门以纠缠量子比特的 2 量子比特量子电路

  4. 将后端设置为我们的本地模拟器:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 运行一次射击的作业:

    counts = execute(qc, backend, shots=1).result().    get_counts(qc)
    
  6. 可视化结果:

    display(plot_histogram(counts))
    

    直方图输出的结果如下:

    Figure 4.17 – The result of a 2-qubit entangled coin toss

    图 4.17 – 2 量子比特纠缠抛硬币的结果

  7. 运行电路几次,你就会意识到在这个双量子抛硬币的结果上,唯一的结果是 img/Formula_04_046.pngimg/Formula_04_047.png。现在再次运行电路,但使用 1,000 次射击,得到以下结果:

    counts = execute(qc, backend, shots=1000).result().get_counts(qc)
    

    你将看到以下直方图作为你代码的结果:

Figure 4.18 – Result of 1,000 shots. Only  and  appear!

img/Figure_4.18_B14436.jpg

图 4.18 – 1,000 次射击的结果。只有 img/Formula_04_048.pngimg/Formula_04_049.png 出现!

它是如何工作的...

让我们从新的门,CX 门开始。这个门将第一个量子比特的量子状态(如第一个量子比特得分的点所示)作为输入,然后如果量子状态是 img/Formula_04_007.png,则在第二个量子比特上执行一个 NOT 门(x)。如果量子状态是 img/Formula_04_002.png,则对第二个量子比特不做任何操作。

所以,这个故事中真正令人兴奋的部分是,在我们预期cx门起作用的地方,我们还没有知道第一个量子比特的状态。Hadamard 门已经将它置于一个很好的叠加态,精确地悬浮在 img/Formula_04_002.pngimg/Formula_04_001.png 之间。直到我们测量第一个量子比特,我们才会知道量子比特处于什么状态。甚至自然也不知道状态,因为量子比特不在特定的状态中;它处于两个状态的叠加中。

那么,CX 门是如何知道在运行程序时需要将第二个量子比特从 img/Formula_04_002.png 翻转到 img/Formula_04_001.png 呢?嗯,这正是量子计算中令人着迷的部分, 没有。只有当我们测量第一个量子比特时,门执行才会发生,纠缠的第二个量子比特才会翻转,或者不会翻转。爱因斯坦将量子力学的这个非常真实的事例称为“遥远地点的幽灵行动”并希望与之无关。

因此,运行我们的小食谱的最终结果是我们得到这两个量子比特的两个可能结果之一,img/Formula_04_054.pngimg/Formula_04_055.png,概率大致相等。如果第一个量子比特读取 img/Formula_04_002.png,则第二个量子比特也将读取相同,给出 img/Formula_04_057.png 的结果。对于 img/Formula_04_001.png 的相反读取也是一样;两个量子比特都将读取相同,给我们 img/Formula_04_059.png 的结果。一旦你读取了一个量子比特,你立即就知道第二个量子比特会是什么。这就是量子抛硬币作弊的方法!

还有更多...

但贝尔态不仅仅锁定在两个结果 img/Formula_04_060.pngimg/Formula_04_061.png 上。通过使用其他门,你可以设置这种纠缠状态为 img/Formula_04_062.pngimg/Formula_04_063.png

{'10': 542, '01': 458}

你会使用哪个门,它在哪个量子比特上起作用,你会在电路的哪个位置添加它?在这种情况下,你可能想要以不同的状态开始量子比特,看看会发生什么。参考“实现倒立抛硬币”菜谱以获取灵感。

参考以下内容

qasm_simulator(模拟一个完美的通用容错量子计算机)上运行这个贝尔态电路得到的结果非常明确,两个量子比特在每次测量时都是相同的。

在现实世界中,现有的物理量子计算机还远未达到通用容错量子计算机的水平,结果将会有所不同。为了快速了解,请参阅“抛一些真实的硬币”菜谱。

更多量子作弊的方法 – 调整概率

在前面的菜谱中,我们使用了一种名为纠缠的量子现象来通过抛硬币作弊。诚然,这可能会很复杂,人们确实倾向于怀疑那些戴着耳机、显然在接住并揭示硬币(测量量子比特)之前就监听信息的抛硬币者。

但剥猫的方法不止一种。记得我们关于量子比特和量子门的讨论。通过使用门来操作量子比特,我们可以在测量之前调整量子比特的状态。向量越接近 img/Formula_04_002.pngimg/Formula_04_001.png,当你测量时,该特定结果出现的概率就越高。

在这个菜谱中,我们将使用旋转门,即Ry门,来增加抛硬币时得到反面结果的概率。

准备工作

这个菜谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r6_coin_toss_rot.py

如何做...

按照前面的例子设置你的代码,然后添加一个Ry门来旋转量子比特:

  1. 导入我们需要的类和方法:

    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    from math import pi
    
  2. 使用一个量子比特和一个经典比特设置我们的量子电路,并基于寄存器创建量子电路:

    qc = QuantumCircuit(1, 1)
    
  3. 将 Hadamard 门、Ry 门和测量门添加到电路中:

    qc.h(0)
    qc.ry(pi/8,0)
    qc.measure(0, 0)
    display(qc.draw('mpl'))
    

    前面的代码应该产生以下输出:

    ![图 4.19 – 带有 Ry 门的我们的作弊抛硬币电路 img/Figure_4.19_B14436.jpg

    图 4.19 – 带有 Ry 门的我们的作弊抛硬币电路

  4. 将后端设置为我们的本地模拟器:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 使用一千次射击运行作业:

    counts = execute(qc, backend, shots=1000).result().    get_counts(qc)
    
  6. 可视化结果:

    display(plot_histogram(counts))
    

    前面代码的直方图看起来如下:

![图 4.20 – 我们作弊抛硬币电路的略微倾斜的结果img/Figure_4.20_B14436.jpg

图 4.20 – 我们作弊抛硬币电路的略微倾斜的结果

好吧,所以现在似乎有更多的机会让你投掷一个 – 几乎是 2 比 1。

它是如何工作的...

那么,这里发生了什么?通过添加 Ry 门,我们极大地提高了我们获胜的机会。让我们更仔细地看看这个门的作用。

让我们看看我们正在讨论的三个不同状态下的 Bloch 矢量。在示例代码中,有一个可疑的函数我们还没有涉及。我们将在 第六章理解 Qiskit® 量子门库中看到更多。通过调用 get_psi() 函数并使用你正在构建的量子电路,你可以看到量子比特在构建电路的每个阶段的动态。该函数使用另一个模拟器,statevector_simulator,它计算你在电路中给定位置的量子比特行为,然后我们使用 plot_bloch_multivector() 方法将其显示为 Bloch 球:

# Function that returns the statevector (Psi) for the circuit
def get_psi(circuit):
    show_bloch=False
    if show_bloch:
        from qiskit.visualization import plot_bloch_multivector
        backend = Aer.get_backend('statevector_simulator') 
        result = execute(circuit, backend).result()
        psi = result.get_statevector(circuit)
        print(title)
        display(qc.draw('mpl'))
        display(plot_bloch_multivector(psi))  

当我们用量子电路作为输入调用此函数时,它将在电路结束时返回量子比特状态矢量(psi 或 )。如果电路中包含多个量子比特,该函数将包括所有量子比特的完整状态矢量。

要使用 get_psi() 函数运行电路,将 show_bloch 变量从 False 更改为 True,然后再次运行你的电路。现在的输出应该看起来像以下这样:

  1. 首先,我们展示了处于基态的量子比特:图 4.21 – 在初始基态,量子矢量直接指向    如我们所预期的那样,高达

    图 4.21 – 在初始基态,量子矢量如我们所预期的那样直接指向

  2. 然后,我们展示了经过 Hadamard 门后的量子比特,现在处于 的叠加态:图 4.22 – 应用 Hadamard 门后,矢量现在指向 Bloch 球的赤道

    图 4.22 – 应用 Hadamard 门后,矢量现在指向 Bloch 球赤道

  3. 我们之前见过这个。如果我们现在测量它,我们会有大约 50% 的概率得到 图 4.23 – 最后,应用 Ry 门后,矢量现在指向 Bloch 球赤道以下    the Bloch sphere equator

图 4.23 – 最后,应用 Ry 门后,矢量现在指向 Bloch 球赤道以下

现在,我们应用 Ry 门,将矢量 弧度(在我们先前的电路图中为 0.3927)绕 y 轴旋转到

因此,这里发生的情况是,我们通过使用 Ry 门修改了角度

记得第二章中的公式,使用 Python 进行量子计算和量子比特

在应用 Hadamard 门之后,角度为(我们还没有围绕y轴旋转)。

测量的概率是:

现在,如果我们通过添加改为,我们得到这个:

这与我们测量的结果非常吻合,表明我们确实调整了量子硬币落地为尾的概率。

还有更多...

有三个基本的旋转门——Rx、Ry 和 Rz,你可以使用它们轻松地将向量指向 Bloch 球上的任何一点。这些在第六章中描述得更详细,理解 Qiskit®门库

尝试这些变体

尝试使用这些创建几个量子电路:

  1. 不使用 Hadamard 门创建一个 1 比特叠加态。

  2. 测量电路以确保结果与使用 Hadamard 门的结果相同。

  3. 使用 Hadamard 门创建一个 1 比特叠加态,然后使用 R 门将向量指向y轴。

    那会是哪个门,输入是什么?你能通过跳过 Hadamard 门而只使用 R 门得到相同的结果吗?哪一个?现在,测量一下,看看这会不会对叠加态电路的预期结果产生影响。

  4. 看看计算测量概率的公式,看看你是否可以使用 Ry 门设置一个电路,使其结果为

    99%的时间

    66%的时间

    33%的时间

恭喜!你现在不仅可以在量子硬币投掷中作弊,还可以计算你的概率。

关于get_psi()函数的更多内容

我们以这个非常小的,1 比特量子电路为例,展示了如何使用我们自制的get_psi()函数逐步遍历你的电路,以了解你的量子比特在每个阶段的运行情况。记住,尽管人们有时会谈论量子比特同时为 0 和 1,但他们真正指的是我们之前看过的叠加数学。重要的是,在我们的计算中,通过操纵它们的态矢量,我们将量子比特置于非常知名的状态。

对于小电路,想象每个步骤发生的事情相对容易,但对于大电路,你很快就会用尽脑力来心理上可视化小量子比特的行为。

您可以使用 get_psi() 函数和状态向量模拟器来完成这个任务。您可以从电路设计的任何位置调用它,以查看您的量子比特正在做什么。如果您的量子程序没有按照您预期的行为运行,请使用状态向量模拟器和布洛赫球可视化来调试。

随着我们继续阅读本书,我们将调整和修改 get_psi() 函数以满足我们的需求,并使用它来显示有关我们电路的更多详细信息。

添加更多硬币 – 正直和作弊

到目前为止,我们的菜谱主要是 1-或 2-量子比特的。有了我们的模拟器,我们可以在电路中随意添加更多量子比特,前提是每个额外的量子比特将需要更多和更多的处理能力,这是您的模拟器运行的系统所必需的。例如,IBM Quantum Experience® qasm_simulator 在 IBM POWER9™ 服务器上运行,最大可达到约 32 个量子比特。

在这个菜谱中,我们将创建两个 3-量子比特量子程序,一个是多硬币投掷,另一个是新纠缠态,称为 GHZ(代表 Greenberger–Horne–Zeilinger 态)。

而不是通过创建两个单独的文件来做这件事,我们将查看一个新的命令,reset()。正如其名所示,使用 reset() 命令与量子比特一起将其设置回其原始状态!,准备开始新一轮的量子计算。在这个例子中,我们使用 reset() 来连续运行两个量子程序,写入两组三个经典寄存器,每次运行测量两次。

准备工作

此菜谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r7_three_coin_toss_ghz.py.

如何操作...

按照前面的示例设置您的代码,但使用三个量子比特和经典比特:

  1. 导入我们需要的类和方法:

    from qiskit import QuantumCircuit, Aer, execute
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    
  2. 使用三个量子比特和六个经典比特设置我们的量子电路:

    qc = QuantumCircuit(3, 6)
    
  3. 将哈达玛门和测量门添加到电路中:

    qc.h([0,1,2])
    qc.measure([0,1,2],[0,1,2])
    display(qc.draw('mpl'))
    

    前面代码的输出如下所示:

    图 4.24 – 一个 3-量子比特叠加电路,测量到经典比特 0、1 和 2

    图 4.24 – 一个 3-量子比特叠加电路,测量到经典比特 0、1 和 2

  4. 将后端设置为我们的本地模拟器:

    backend = Aer.get_backend('qasm_simulator')
    
  5. 使用一千次射击运行作业:

    counts = execute(qc, backend, shots=1000).result().    get_counts(qc)
    
  6. 可视化结果:

    display(plot_histogram(counts))
    

    前面代码的直方图如下所示:

    图 4.25 – 三个叠加态的量子比特给出随机结果

    图 4.25 – 三个叠加态的量子比特给出随机结果

    注意到这里我们只看到了前三个经典比特(0、1 和 2)的测量结果。最后三个经典比特都是 0。

  7. 现在通过重置量子比特,向量子比特 0 添加一个 Hadamard 门,然后添加两个 CX(受控非)门,一个从量子比特 0 到量子比特 1,另一个从量子比特 0 到量子比特 2 来修改电路。

    我们将使用一个新的电路命令来重置电路的量子比特回到初始状态,并使用reset()重新开始:

    qc.barrier([0,1,2])
    qc.reset([0,1,2])
    qc.h(0)
    qc.cx(0,1)
    qc.cx(0,2)
    qc.measure([0,1,2],[3,4,5])
    display(qc.draw('mpl'))
    

    记住,当你只修改一个量子比特时,你必须指定要修改的量子比特,就像这样qc.h(0),向第一个量子比特添加 Hadamard 门:

    ![图 4.26 – 将量子电路的量子比特重置为初始状态并重新开始,将最终测量写入经典比特 3、4 和 5 图片

    图 4.26 – 将量子电路的量子比特重置为初始状态并重新开始,将最终测量写入经典比特 3、4 和 5

  8. 使用 1,000 次射击运行作业:

    counts = execute(qc, backend, shots=1000).result().    get_counts(qc)
    
  9. 显示结果:

    display(plot_histogram(counts))
    

    之前代码的直方图展示如下:

![图 4.27 – 三个抛掷硬币量子比特和三个纠缠量子比特(GHZ 态)在第一三个和第二三个经典比特中的组合结果图片

图 4.27 – 三个抛掷硬币量子比特和三个纠缠量子比特(GHZ 态)在第一三个和第二三个经典比特中的组合结果

它是如何工作的...

我们创建的两个电路并没有什么本质上的新东西,只是增加了更多的量子比特,以及两轮测量。在第一个中,我们并行添加了所有门,而在第二个中,我们仔细指定了添加门的位置。最终结果是两个 3 量子比特电路,一个代表了更多抛掷的硬币,另一个扩展了我们之前探索的贝尔态。

最后一个很有趣,因为它显示了几个量子比特之间的纠缠;现在没有一个量子比特可以单独处理。这种纠缠是更高级量子算法的关键,在这些算法中,大量量子比特被设置,处于叠加态,然后被纠缠。它们最终被其他门作用,以产生特定的结果。

最后,我们测试了一种新的将结果写入经典比特的方法。我们将抛掷硬币的结果写入前三个经典比特,将 GHZ 状态的结果写入最后三个。

更多...

进行一些实验,看看你如何构建具有更多量子比特的更大电路,以及如何添加门:

  1. 创建一个 5 量子比特电路,所有量子比特上都有 Hadamard 门,但只有第一个、第三个和第五个量子比特上有测量门。你实际上需要多少个经典寄存器?

  2. 创建一个 5 量子比特电路,将第一个、第二个、第四个和第五个量子比特与第三个量子比特纠缠。你需要在哪个量子比特上添加 Hadamard 门?

  3. 我们在本菜谱中构建的 GHZ 电路只能给出纠缠结果![img/Formula_04_089.png]和![img/Formula_04_090.png]。构建一个给出结果![img/Formula_04_091.png]或![img/Formula_04_092.png]的电路。除了 H 和 CX 门之外,你使用什么门,以及你将它们放在哪里,或者它们在哪里?

    小贴士

    如果你只使用一组测量命令,而不使用reset()命令,可能更容易理解这些电路的工作原理。

投掷一些真实的硬币

那么,你的想法是什么?你现在是否完成了量子硬币投掷的模拟,并想要进行真正的操作,即在真实的 IBM 量子计算机上运行你的 Qiskit®量子程序?让我们最终利用你创建的 IBM Quantum Experience® API 密钥。

在本菜谱中,你将通过使用 Qiskit®在真实的 IBM Quantum®机器上运行作弊硬币投掷或贝尔态。我们知道在完美模拟的量子计算机上预期的结果是什么,现在我们将看看所谓的 NISQ 机器的实际结果是什么样的。

最后,我们将利用这个 API 密钥,介绍 IBMQ 组件,如何找到后端,如何选择最佳后端,以及如何在该后端运行电路。

准备工作

本菜谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter04/ch4_r8_coin_toss_IBMQ.py.

要能在真实的 IBM 硬件上运行你的量子程序,你需要使用你在 IBM Quantum Experience®账户中分配的 API 密钥。如果你在 IBM Quantum Experience®笔记本环境中运行 Qiskit®,那么你的 API 密钥已经可用,无需进一步操作。

如果你在自己的本地机器上运行 Qiskit®,你必须将 API 密钥本地存储。你可能已经作为第一章中“安装你的 API 密钥和访问你的提供商”菜谱的一部分完成了这些必要的步骤,准备你的环境。如果没有,现在就完成这个任务。我们在这里等你。

如何操作

按照前面的示例设置你的代码,使用两个量子比特和经典比特:

  1. 导入所需的类和方法:

    from qiskit import QuantumCircuit, execute
    from qiskit import IBMQ
    from qiskit.tools.monitor import job_monitor
    from IPython.core.display import display
    
  2. 获取存储的 API 密钥:

    IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  3. 使用两个量子比特和两个经典比特设置我们的量子电路:

    qc = QuantumCircuit(2, 2)
    
  4. 添加 Hadamard 门和 CX 门以准备贝尔态:

    qc.h(0)
    qc.cx(0,1)
    qc.measure([0,1],[0,1])
    display(qc.draw('mpl'))
    

    上述代码显示了以下电路:

    ![图 4.28 – 2 量子比特贝尔态电路

    ![img/Figure_4.28_B14436.jpg]

    图 4.28 – 2 量子比特贝尔态电路

  5. 将后端设置为最不繁忙的 IBM Quantum®机器:

    from qiskit.providers.ibmq import least_busy
    backend = least_busy(provider.backends(n_qubits=5,    operational=True, simulator=False))
    print(backend.name())
    ibmq_essex
    

    我们将在第五章的“寻找最不繁忙的后端”食谱中重新审视这种选择 IBM Quantum®计算机后端来运行你的电路的方法,即使用 Qiskit®工具游览 IBM Quantum Hardware®。现在,只需按原样使用代码即可。

  6. 使用 1,000 次射击运行作业。等待作业完成:

    job = execute(qc, backend, shots=1000)
    job_monitor(job)
    Job Status: job has successfully run
    
  7. 获取结果:

    result = job.result()
    counts = result.get_counts(qc)
    from qiskit.tools.visualization import plot_histogram
    display(plot_histogram(counts))
    

    上述代码的直方图表示如下:

![Figure 4.29 – 在实际的 IBM Quantum®后端上运行贝尔态电路的结果img/Figure_4.29_B14436.jpg

Figure 4.29 – 在实际的 IBM Quantum®后端上运行贝尔态电路的结果

稍等一下!我们只期待得到img/Formula_04_093.pngimg/Formula_04_094.png的结果……这里发生了什么?

它是如何工作的...

哇,看看那些有噪声的量子位!

你在程序的有噪声结果中看到的就是噪声本身。尽管 IBM Quantum®计算机在低于外太空温度(15 毫开尔文)下运行,但它们在执行量子位上的门操作和测量时仍然会遭受随机噪声。

我们一直运行量子程序的本地qasm_simulator表现得像一个完美的通用量子计算机。另一方面,真正的硬件被称为有噪声的中规模量子(NISQ)计算机,其表现并不完美。

我们将在第五章使用 Qiskit®工具游览 IBM Quantum®硬件中更详细地探讨现有的 IBM Quantum®后端。

还有更多...

当你在实际的 IBM Quantum®机器上运行你的作业时,通常会有所不同,与你在本地模拟器上所经历的受控体验。

排队

一旦你开始在 IBM Quantum®机器上运行一个或两个量子程序,你很可能会意识到你不是唯一一个。实际上,可供公众免费使用的量子计算机数量有限。截至本文撰写时,有稳定增长数量的物理后端和一个模拟器可以使用。使用provider.backends()命令列出目前可用的后端:

from qiskit import IBMQ
IBMQ.load_account()
provider = IBMQ.get_provider()
provider.backends()

上述代码可能得到以下类似的结果:

[<IBMQSimulator('ibmq_qasm_simulator') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmqx2') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_16_melbourne') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_vigo') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_ourense') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_london') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_burlington') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_essex') from IBMQ(hub='ibm-q', group='open', project='main')>,
 <IBMQBackend('ibmq_armonk') from IBMQ(hub='ibm-q', group='open', project='main')>]

记住,你在这里是共享时间,与其他所有人同时使用与你相同的机器。在下一章中,我们将更仔细地查看这些机器,并探讨如何确定在任何给定时刻使用哪个机器最好。

结果中的硬件细节

作业完成后,你已经查看过结果后,可以通过使用job.result()命令快速查看完整的返回结果。它看起来可能像这样:

namespace(backend_name='ibmq_burlington',
          backend_version='1.1.4',
          qobj_id='630c02ca-7d06-4430-91e8-8ef08b9f5a63',
          job_id='5f15dec89def8b001b437dfe',
          success=True,
          results=[namespace(shots=1000,
                        success=True,
                        data=namespace(counts=namespace(                                                       0x0=471,
                                                     0x1=529)),
                        meas_level=2,
                        header=namespace(memory_slots=2,
                                         qreg_sizes=[['q', 5]],
                                         qubit_labels=[['q',0],
                                                       ['q',1],
                                                       ['q',2],
                                                       ['q',3],
                                                      ['q',4]],
                                         name='circuit58',
                                         n_qubits=5,
                                         creg_sizes=[['c', 2]],
                                         clbit_labels=[['c',0],
                                                     ['c',1]]),
                        memory=False)],
          status='Successful completion',
          header=namespace(backend_version='1.1.4',
                        backend_name='ibmq_burlington'),
          date=datetime.datetime(2020, 7, 20, 18, 13, 44, tzinfo=datetime.timezone.utc),
          time_taken=7.400392055511475,
          execution_id='bc4d19d0-cab4-11ea-b9ba-ac1f6b46a78e')

在这个结果集中,你可以看到有关所使用的量子计算机后端的信息,运行的结果,以及运行的状态和日期和时间。

如果等待时间过长怎么办?

有时你前面可能有一个非常长的队列,你必须等待你的轮次。从某种意义上说,IBM Quantum®机器就像计算机早期的时间共享设置一样工作;同一时间只能运行一个程序。

但无需绝望。你的作业已经提交,你可以退后一步,让机器做它们的工作,在你方便的时候继续。

这与 IBM Quantum Experience®电路作曲家的工作方式相同。一旦提交,你的作业将等待它的轮次,然后在返回时显示在结果页面上。要在你的本地 Qiskit®中做同样的事情,你首先需要你的作业 ID。

你可以使用作业 ID 来获取你的最新作业,甚至检索你之前执行过的作业。每个执行过的作业都有一个唯一的作业 ID。

使用job.job_id()命令来获取作业 ID:

job.job_id()
Out[]: '5f15dec89def9b001b437dfe'

现在你有了作业 ID,你可以通过使用retrieve_job()命令来获取job对象:

get_result=backend.retrieve_job(<jobid>)

现在你可以像平时一样从job对象中获取作业结果,例如:

counts = get_result.result().get_counts()
print(counts)

前面的代码可能会产生以下输出:

Out[]: {'11': 339, '10': 174, '00': 339, '01': 172}

你还可以使用作业 ID 来查找作业的状态,并查看它在队列中的当前位置:

print(backend.retrieve_job(<jobid>).status())

这里有一个例子:

Out[]: JobStatus.QUEUED
Out[]: JobStatus.RUNNING
Out[]: JobStatus.DONE

IBM Quantum Experience®中内置了更多与作业相关的功能,你可以从中获取。要更详细地了解后端以及你可以了解的内容,请继续阅读第五章使用 Qiskit®工具游览 IBM Quantum®硬件

第五章:第五章:使用 Qiskit®游览 IBM Quantum®硬件

在前面的章节中,我们主要使用了内置和本地各种形式的量子计算机模拟器,但我们也将一些量子程序连接到实际的 IBM 量子计算机上并运行。在本章中,我们将更深入地研究这些后端,直至实际的物理量子比特层面。

我们将通过使用 IBM Quantum Experience®和 Qiskit®访问有关可用硬件的数据,快速游览 IBM Quantum®实验室。在这些事情中,我们将查看量子芯片布局的图形视图,量子计算机的一些物理方面,如 T1 和 T2 退相干参数,一些基本和高级错误度量,可用的量子比特之间如何相互交互,等等。

在本章中,我们将涵盖以下食谱:

  • IBM Quantum®机器是什么?

  • 定位可用的后端

  • 比较后端

  • 寻找最不繁忙的后端

  • 可视化后端

  • 使用 Qiskit®探索选定的后端

在本章中,我们将一般使用我们在第四章中使用的贝尔态程序,从 Terra 的起点开始,作为其理想结果,,这些结果对我们来说是已知的,我们可以使用它们来比较不同机器和不同量子比特集上的运行。

技术要求

在您开始本章的量子程序之前,请确保您已完成了第一章中所有步骤,准备您的环境,特别是安装您的 API 密钥并访问您的提供商食谱。

本章中讨论的量子程序可以在此找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter05

IBM Quantum®机器是什么?

本节与其说是食谱,不如说是对您将遇到的量子组件和过程的基本概述。如果您想直接跳过并开始编码,请转到下一食谱。

使用 Qiskit®,您可以在两种类型的量子计算机上运行您的量子程序:ibmq_qasm_simulator – 可在线使用 – 允许您在最多 32 个量子比特 上运行相当深入的量子程序。您本地模拟器的性能取决于您的硬件;请记住,模拟量子计算机随着每个量子比特的增加而呈指数级变难。

实际的 IBM 量子计算机硬件位于 IBM 实验室中,并通过云访问。这样做有很好的理由,所以让我们一步步了解如何设置和运行 IBM Quantum®提供的超导量子比特量子计算机。

准备工作

超导量子计算机对噪声非常敏感,如电磁辐射、声波和热量。配备低温冷却的隔离环境提供了一个尽可能少的干扰的位置。

量子计算机可能使用所谓的约瑟夫森结,在低温下保持并受微波脉冲操控。普通人没有这种硬件,因此,在这本书中,我们将使用云中免费可用的 IBM 量子计算机进行量子编程。

如何操作...

以下步骤是对在真实的 IBM 量子计算机上运行你的量子程序过程的非常高级的描述:

  1. 在你的本地 Qiskit®环境中或在 IBM Quantum Experience®上编写量子程序。

  2. 将你的程序通过云发送到 IBM Quantum®进行排队。

    IBM 云®量子计算模型意味着你将不会无限制地访问这些机器。这是一个时间共享系统,每个人都会得到一些访问权限,类似于计算机早期使用过的经典时间共享系统。这种类比并不太坏。

  3. 你的程序现在获得了u1u2u3idcx。结果证明,你编写的所有量子程序都可以仅使用这些门来表示。

    作为转换的一部分,你的电路可能会在大小和深度上有所变化,因为单个门根据你运行程序的底层转换成一系列门。简单来说,电路的大小是使用的门的基本数量,深度是电路从左到右的长度,这大致相当于量子计算机运行你的程序时必须进行的并行操作的数量。你的原始程序结构可能需要改变以适应你运行的芯片的物理布局。

  4. 你的转换程序中的门被编码成波包。

    现在代码已经被转换成可以在芯片上运行的组件,这些组件被转换成微波包,可以发送到量子芯片。每个门可以看作是量子比特布洛赫矢量绕三个轴的旋转,每个角度都可以编码为不同频率和持续时间的微波脉冲。

  5. 量子芯片被重置。

    在我们能在芯片上执行任何量子计算之前,其量子比特需要重置到其基态。这是通过向每个量子比特发送特定的微波脉冲来完成的,就像在下一步中描述的将你的门发送到量子比特一样。

  6. 你的编码门随后被发送到量子比特。

    每个门都作为波包发送到相应的量子比特,这个波包位于一个 GHz 载波波上,其频率正好调谐到接收量子比特的频率。我们现在离开了所谓的室温电子设备,进入了低温环境。编码门的信号通过依次冷却的层向下传输到量子计算机的内部,最终到达 15 毫开尔文温度的量子芯片——比外太空还要冷。在旅程结束时,波包最终通过微波谐振器撞击量子比特,以改变量子比特的状态。

    这对每个量子比特上应用的每个门都重复进行,这就是在后端运行量子程序的过程。

  7. 现在读取了量子比特。

    到程序结束时,一种特定的波包与谐振器发生干涉,然后产生的包干涉被发送回堆栈,通过依次变暖的层,然后进入室温电子设备。

    干涉被解释为01,因此你的程序结果被记录下来。在这个状态下,你共振量子比特的微妙平衡已经被破坏——量子比特不再表现出量子行为,在我们可以再次使用它之前需要将其重置到基态。

整个过程会根据你要求的射击次数重复进行,所有结果都存储在云端。最后,如果你的耐心足够,你的完整运行会被打包并返回给你,同时也会被存储以供以后检索。

它是如何工作的...

之前提到的多数步骤都是高度自动化的。你只需编写你的量子程序并发送出去,然后 IBM Quantum®会完成剩余的工作,并将你的量子比特测量结果以 1 或 0 的形式返回。

如你所见,有几个步骤你可以介入并指定如何操作,例如,选择后端,根据量子比特参数选择要使用的量子比特,决定要运行的射击次数,等等。在本章中,我们将介绍如何从 IBM Quantum®机器中挖掘硬件信息和配置参数。

参考信息

定位可用的后端

在 Qiskit®中,后端代表您运行量子程序的系统。一个 后端 可以是一个模拟器,比如我们之前使用过的本地 Aer 模拟器。如果您想在本地模拟器而不是在您的本地模拟器上运行量子程序,您必须将一个 IBM Quantum®机器识别为后端来使用,然后配置您的量子程序以使用它。

让我们看看我们将要进行的步骤:

  1. 首先,导入所需的类和方法,并加载您的账户信息。在这种情况下,我们使用 IBM Q 类,它包含主要的硬件相关功能。

  2. 查看您账户可用的机器。

  3. 选择一个通常可用的后端。

  4. 在选定的后端上创建并运行贝尔态量子程序。

  5. 选择一个模拟器后端,再次运行贝尔态量子程序以进行比较。

准备工作

在这个菜谱中,我们将使用 IBM Q provider.backends() 方法来识别和过滤可用的后端以运行您的程序,然后使用 provider.get_backend() 方法来选择后端。在下面的示例中,我们将使用 ibmqx2ibmq_qasm_simulator 后端。然后我们将在一个硬件后端上运行一个小型量子程序,然后是在模拟器后端上。

在以下菜谱中的 Python 文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter05/ch5_r1_identifying_backends.py

如何做到这一点...

  1. 总是先导入 Qiskit®类和方法以使用:

    from qiskit import IBMQ, QuantumCircuit, execute
    from qiskit.tools.monitor import job_monitor
    
  2. 在您可以使用 IBMQ 类和后端方法(如果尚未设置)之前,您必须设置与您的账户关联的提供者:

    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  3. provider.backends() 方法用于定位对您的 IBM Quantum®账户可用的 IBM Quantum®后端。有了这些信息,您可以通过使用 provider.get_backend() 方法来设置您想要在它上面运行量子程序的后端:

    print(provider.backends(operational=True, 
        simulator=False))
    
  4. 之前的代码可能会产生类似于以下输出的结果:

    Available backends:
    [<IBMQBackend('ibmqx2') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_16_melbourne') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_vigo') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_ourense') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_valencia') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_london') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_burlington') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_essex') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_armonk') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_santiago') from IBMQ(hub='ibm-q', group='open', project='main')>] 
    >>> provider.backends(simulator=False)
    

    或者,您可以使用复杂的过滤,如 lambda 函数:

    >>> provider.backends(filters=lambda x: not x.configuration().simulator)
    

    我们也只对那些没有因维护而关闭的后端感兴趣。为此,我们通过 operational 参数进行过滤:

    >>> provider.backends(operational=True, simulator=False)
    

    或者,您可以使用以下代码:

    >>> provider.backends(filters=lambda x: not x.configuration().simulator and x.status().operational)
    
  5. 因此,考虑到这一点,当你想在 IBM Quantum®机器上运行你的量子程序时,你需要指定要运行的后端,为此,你可以使用get_backend()方法。让我们手动从我们之前的列表中选择一个后端,例如,ibmqx2

    backend = provider.get_backend('ibmqx2')
    print("\nSelected backend:", backend.name())
    
  6. 前面的代码应该给出以下结果:

    Out[]: Selected backend: ibmqx2
    
  7. 现在,选择了一个后端,你可以使用命令job = execute(<your_quantum_circuit>, backend)在该后端上执行作业。在这种情况下,我们将使用以下命令:

    job = execute(qc, backend, shots=1000)
    
  8. 我们现在可以创建电路进行测试:

    qc = QuantumCircuit(2,2)
    qc.h(0)
    qc.cx(0,1)
    qc.measure([0,1],[0,1])
    print("\nQuantum circuit:")
    print(qc)
    job = execute(qc, backend, shots=1000)
    job_monitor(job)
    result = job.result()
    counts = result.get_counts(qc)
    print("\nResults:", counts)
    
  9. 示例代码应该给出类似以下的结果:图 5.1 – 在所选后端上贝尔量子电路的输出

    图 5.1 – 在所选后端上贝尔量子电路的输出

  10. 要选择 IBM Quantum®模拟器作为后端,并在其上运行电路,你可以这样做:

    print(provider.backends(operational=True,
        simulator=True))
    backend = provider.get_backend('ibmq_qasm_simulator')
    job = execute(qc, backend, shots=1000)
    job_monitor(job)
    result = job.result()
    counts = result.get_counts(qc)
    print("\nSimulator results:", counts)
    
  11. 前面的代码应该给出以下结果:

图 5.2 – 在 ibmq_qasm_simulator 后端上贝尔量子电路的输出

图 5.2 – 在 ibmq_qasm_simulator 后端上贝尔量子电路的输出

因此,我们现在已经确定了可用的 IBM Quantum®后端,并在我们选择的后端以及模拟后端上运行了一个量子程序。在下一道菜谱“比较后端”中,我们将对可用的后端进行简单的性能比较。

更多内容...

你还可以使用backends()方法查看你本地安装的 Qiskit®环境中可用的模拟后端。首先,导入Aer模拟器类,然后使用backends()方法查看可用的后端:

from qiskit import Aer
Aer.backends()

前面的代码应该给出以下结果:

Out[]: [<QasmSimulator('qasm_simulator') from AerProvider()>,
 <StatevectorSimulator('statevector_simulator') from 
 AerProvider()>,
 <UnitarySimulator('unitary_simulator') from AerProvider()>,
 <PulseSimulator('pulse_simulator') from AerProvider()>]

这些都是模拟器:

  • 'qasm_simulator':这个模拟器允许你运行你的量子程序,并得到的结果就像你在没有错误和噪声的完美量子计算机上运行一样。

  • 'statevector_simulator':使用这个,你可以模拟你的量子比特在整个电路中的状态向量。

  • 'unitary_simulator':这个模拟器允许你为你的电路创建单位矩阵。

  • 'pulse_simulator':这个模拟器允许你向量子比特发送离散脉冲。

我们已经看到了在第四章,“从 Terra 的起点开始”,中使用的'qasm_simulator''statevector_simulator',我们将在第六章,“理解 Qiskit®门库”中更详细地查看'unitary_simulator'

参见

比较后端

IBM Quantum®后端在数量、比特数以及这些比特之间的行为和交互上都有所不同。根据你编写的量子程序,你可能希望在具有特定特性的机器上运行代码。

IBMQ 返回的后端信息只是一个普通的 Python 列表,你可以用任何其他列表来操作返回的数据。例如,你可以编写一个 Python 脚本来查找可用的 IBM Quantum®后端,然后在每个后端上运行量子程序,并在显示后端比特质量大致测量的图表中比较结果。

在这个菜谱中,我们将使用一个简单的 Python 循环,在可用的 IBM Quantum®后端上运行一系列相同的贝尔态量子程序,以获得后端性能的大致估计。

准备工作

用于此菜谱所需的文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter05/ch5_r2_comparing_backends.py

如何操作…

让我们看看如何比较后端:

  1. 首先,我们导入所需的类和方法。

    在这种情况下,我们使用 IBMQ 库,它包含主要的硬件相关函数。我们还导入了构建量子电路、监控作业和显示结果的类。然后加载存储的账户 API 密钥并获取提供者:

    from qiskit import IBMQ, QuantumCircuit, execute
    from qiskit.tools.monitor import job_monitor
    from qiskit.visualization import plot_histogram
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 现在,我们将创建一个已知预期结果的量子程序。

    例如,一个贝尔态程序,在完美的量子计算机上只会给出 的结果:

    qc = QuantumCircuit(2,2)
    qc.h(0)
    qc.cx(0,1)
    qc.measure([0,1],[0,1])
    
  3. 上一段代码应该给出以下结果:![图 5.3 – 一个贝尔态量子电路 图 5.3 – 后端信息

    图 5.3 – 一个贝尔态量子电路

  4. 现在,我们将获取所有可用和可操作的后端,包括用于基准测试的 IBM Quantum®模拟器:

    backends = provider.backends(filters=lambda b:
        b.configuration().n_qubits > 1 and
            b.status().operational)
    print("\nAvailable backends:", backends)
    

    过滤掉单比特后端

    看看我们在这里是如何使用过滤器,只包括具有超过 1 个比特的后端。这样做的原因是我们的代码需要两个操作比特,如果我们在只有一个比特的后端上运行代码,代码将会失败。可用的 IBM Quantum®机器之一—ibmq_armonk—是一个只有一个比特的量子计算机;我们不希望使用它,并使用过滤器将其从我们的后端列表中删除。ibmq_armonk后端的目的在于进行量子比特脉冲编程的实验,这超出了本书的范围。

  5. 上一段代码可能会得到以下结果:

    Available backends: [<IBMQSimulator('ibmq_qasm_simulator') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmqx2') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_16_melbourne') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_vigo') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_ourense') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_valencia') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_london') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_burlington') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_essex') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_santiago') from IBMQ(hub='ibm-q', group='open', project='main')>]
    
  6. 然后,我们将按顺序在这些后端上运行简单的量子程序。结果计数存储在一个我们称之为counts的字典中:

    counts = {}
    for n in range(0, len(backends)):
        print('Run on:', backends[n])
        job = execute(qc, backends[n], shots=1000)
        job_monitor(job)
        result = job.result()
        counts[backends[n].name()] = result.get_counts(qc)
    

    排队

    在几台不同的机器上运行四个量子程序可能需要一段时间,具体取决于其他用户在积极使用后端的人数以及排队作业的数量。例如,在典型的周日晚上,在八个后端和一个模拟器上运行此程序可能需要大约一个小时。

  7. 之前的代码可能会得到以下结果:

    Run on: ibmq_qasm_simulator
    Job Status: job has successfully run
    Run on: ibmqx2
    Job Status: job has successfully run
    Run on: ibmq_16_melbourne
    Job Status: job has successfully run
    ...
    Run on: ibmq_essex
    Job Status: job has successfully run
    Run on: ibmq_santiago
    Job Status: job has successfully run
    
  8. 现在作业正在运行,我们可以使用以下代码打印并绘制结果:

    print("\nRaw results:", counts)
    #Optionally define the histogram colors.
    colors = ['green','darkgreen','red','darkred','orange',
        'yellow','blue','darkblue','purple']
    #Plot the counts dictionary values in a histogram, using
    #the counts dictionary keys as legend.
    display(plot_histogram(list(counts.values()),
        title = "Bell results on all available backends
        legend=list(counts), color = colors[0:len(backends)],
        bar_labels = True)
    
  9. 之前的代码可能会得到以下结果:

    Raw results: {'ibmq_qasm_simulator': {'00': 510, '11': 490}, 'ibmqx2': {'00': 434, '01': 77, '10': 39, '11': 450}, 'ibmq_16_melbourne': {'00': 474, '01': 42, '10': 48, '11': 436}, 'ibmq_vigo': {'00': 512, '01': 18, '10': 42, '11': 428}, 'ibmq_ourense': {'00': 494, '01': 26, '10': 19, '11': 461}, 'ibmq_valencia': {'00': 482, '01': 31, '10': 30, '11': 457}, 'ibmq_london': {'00': 463, '01': 48, '10': 39, '11': 450}, 'ibmq_burlington': {'00': 385, '01': 182, '10': 84, '11': 349}, 'ibmq_essex': {'00': 482, '01': 46, '10': 24, '11': 448}, 'ibmq_santiago': {'00': 514, '01': 17, '10': 17, '11': 452}}
    

这些原始结果显示了每个后端运行程序的效果。ibmq-qasm-simulator 代表在模拟的通用量子计算机上的理想结果;其他结果显示了程序在实际 IBM Quantum® 后端上的运行情况。一个完美的量子计算机将得到与模拟器相似的结果,从而得到 的值:

图 5.4 – 贝尔结果的图形视图

图 5.4 – 贝尔结果的图形视图

我们在这里创建的是一个简单的比较,使用一个 基本的 2 量子比特贝尔态量子程序来比较可用的 IBM Quantum® 后端。在一个完美的量子计算机上,我们只期望得到结果 – – 这正是我们在 ibmq_qasm_simulator 上看到的结果。正如我们在第四章从 Terra 的基础开始所讨论的,对于真实的 有噪声的中尺度量子NISQ)机器,我们期望有一些噪声和混合结果,包括 ,和 对于 IBM Quantum® 硬件。

一般而言, 条形图越小,后端的表现越好,但有许多因素在起作用。这些将在本章的后面以及第八章用 Ignis 清理你的量子行为中探讨。

此外,请注意,我们在这里只比较了你的量子程序默认执行情况。不同的量子比特配置、读出错误、量子比特连接问题和其他在真实硬件上发生的错误也会起作用,并使结果变得有些随机。

更多内容

在这个配方中,我们在几乎所有可用的后端上运行了我们的量子程序。正如你在 定位可用的后端 配方中所看到的,你还可以过滤后端。这个配方至少需要两个量子比特来运行,因此我们添加了一个过滤器来包括具有一个以上量子比特的后端:

backends = provider.backends(filters=lambda b: b.configuration().n_qubits > 1 and b.status().operational)

你可以使用过滤功能出于其他原因;例如,仅运行在 5 量子比特机器上,通过量子比特数量进行过滤:

# Get all available and operational backends.
backends = provider.backends(n_qubits=5)

你现在可以看到可用的后端如何表现,以及其中一些可能有多忙碌。是时候找出一种方法,通过找到队列最短的后端来加速你的量子程序执行了。

寻找最近忙的后端

当您在 IBM Quantum®后端上运行量子程序时,您会很快意识到,您可能不是唯一一个同时尝试使用相同后端的人。根据时间和星期几,以及正在运行的量子程序的类型和目的,并非所有 IBM Quantum®机器的使用程度都相同。

如果您不关心应该在哪个机器上运行代码,您可以使用least_busy方法自动找到运行程序的最佳后端。后端的最不繁忙状态通常意味着您有最少的等待时间,但这并不一定正确。有些程序运行时间比其他程序长,队列(就像在超市里一样)可能以不同的速度移动。

让我们看看以下步骤:

  1. 首先,导入所需的IBMQleast_busy方法,并加载我们的账户。

  2. 使用least_busy方法自动选择通常可用的最不繁忙的后端,以及具有 5 量子比特的最不繁忙的后端。

  3. 最后,让我们显示所有后端的概述数据,以验证我们选择的后端确实是最近忙的后端。

如果我们不希望等待执行但可能不是最佳选择,least_busy方法就非常方便。Qiskit®已经为我们提供了最不繁忙的后端名称。我们现在可以信任 Qiskit®并在该后端上运行我们的量子代码,或者,根据我们的目的,我们可能选择另一个后端来运行。结果可能是,最不繁忙的后端是一个具有噪声量子比特或 T1 或 T2 时间短的机器,这并不适合我们的需求。

准备工作

下面的食谱中所需文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter05/ch5_r3_least_busy.py

如何做到这一点...

least_busy正在寻找包含pending_jobs参数的后端。如果您添加了额外的筛选功能,排除了实际最不繁忙的后端,该方法将返回符合筛选标准的具有最少挂起作业的后端。

让我们看看:

  1. 首先,导入所需的IBMQleast_busy方法,并加载我们的账户:

    from qiskit import IBMQ
    from qiskit.providers.ibmq import least_busy
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 现在,我们可以询问 IBM Quantum®哪个后端是最不繁忙的,并相应地设置我们的backend参数:

    backend = least_busy(provider.backends(simulator=False))
    print("Least busy backend:", backend.name())
    

    上述代码可能会得到以下结果:

    Out[]: 
    Least busy backend: ibmq_armonk
    

    在这个例子中,最不繁忙的后端是ibmq_armonk,这是一台用于脉冲测试的 1 量子比特机器。这不是您想在上面运行多量子比特电路的后端。

  3. 筛选最不繁忙的结果。

    您可以将least_busy方法传入一个经过筛选的后端列表,例如,仅包括 5 量子比特的机器,或者直接使用未经筛选的provider.backends()函数调用该方法,如下面的示例所示:

    filtered_backend = least_busy(provider.backends(
        n_qubits=5, operational=True, simulator=False))
    print("\nLeast busy 5-qubit backend:",
        filtered_backend.name())
    

    以下代码可能给出以下输出:

    Out[]: 
    Least busy 5-qubit backend: ibmq_santiago
    

    就这样 – 这是拥有五个量子比特的最不繁忙的后端。

  4. 为了检查所选方法是否选择了最佳后端,我们可以使用backend_overview()方法查看可用后端的挂起作业数量:

    from qiskit.tools.monitor import backend_overview
    print("\nAll backends overview:\n")
    backend_overview()
    

    以下代码可能给出以下结果:

图 5.5 – 未过滤的所有可用后端

图 5.5 – 未过滤的所有可用后端

注意最不繁忙参数。如您所见,最不繁忙的后端挂起作业数量是最小的。

因此,本食谱的要点是您可以自动化您运行的量子程序的后端,但如果您需要在特定数量的量子比特上运行程序,返回的后端可能不是您想要的。如果是这种情况,通过量子比特数量过滤搜索以获取队列最短的后端。

可视化后端

现在我们已经开始探索 IBM Quantum®后端的各个参数,有一个简单的方法来获取量子芯片和各个重要参数的视觉概述将很有帮助,例如量子比特如何相互连接,哪些连接比其他连接更好,每个量子比特的质量如何,等等。Qiskit®内置了可视化功能。

准备工作

下面的食谱中所需的文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter05/ch5_r4_backend_vis.py

如何操作...

我们将使用qiskit.visualization包的三个方法来查看后端:plot_gate_map()plot_error_map()plot_circuit_layout()。对于最后一个,我们还需要使用transpile()方法对量子电路进行转换,然后显示 Qiskit®将您的门映射到后端上的哪些量子比特。

让我们看看以下步骤:

  1. 首先导入所需的qiskitqiskit.visualization方法,并加载我们的账户:

    from qiskit import IBMQ, QuantumCircuit, transpile
    from qiskit.providers.ibmq import least_busy
    # Import the backend visualization methods
    from qiskit.visualization import plot_gate_map,
    plot_error_map, plot_circuit_layout
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 获取并获取所有具有超过 1 个量子比特且不是模拟器的可用 IBM Quantum®后端:

    available_backends = provider.backends(filters=lambda b:
        b.configuration().n_qubits > 1 and b.status().
            operational)
    print("{0:20} {1:<10}".format("Name","#Qubits"))
    print("{0:20} {1:<10}".format("----","-------"))
    for n in range(0, len(available_backends)):
        backend = provider.get_backend(str(available_
            backends[n]))
        print("{0:20} {1:<10}".format(backend.name(),
            backend.configuration().n_qubits))
    
  3. 选择您想要查看的后端:

    backend_input = input("Enter the name of a backend, or X
        for the least busy:")
    if backend_input not in ["X","x"]:
        backend = provider.get_backend(backend_input)
    else:
        backend = least_busy(provider.backends(
            filters=lambda b: b.configuration().n_qubits > 1
                and b.status().operational))
    
  4. 现在,让我们显示后端的门映射图和错误映射图:

    print("\nQubit data for backend:",backend.status().
        backend_name)
    display(plot_gate_map(backend, plot_directed=True))
    display(plot_error_map(backend))
    

    这种首次可视化显示了后端的逻辑布局,可选地显示了量子比特之间允许的通信方向(plot_directed=True)。

    考虑以下示例:display(plot_gate_map(backend, plot_directed=True))

    以下代码可能为ibmq_burlington提供以下输出:

    图 5.6 – ibmq_burlington 的门映射图

    图 5.6 – ibmq_burlington 的门映射图

    通过错误映射可视化,你可以看到后端的读出错误和 CX 错误率。此图为你提供了关于量子比特质量的信息,这些量子比特可以提供准确的读出结果,并在两个量子比特之间的 受控-NOT (CX) 门上正确执行:

    Figure 5.7 – ibmq_burlington 的错误映射

    图 5.7 – ibmq_burlington 的错误映射

  5. 最后,创建一个贝尔电路,将其翻译,并使用它来显示电路布局:

    # Create and transpile a 2 qubit Bell circuit
    qc = QuantumCircuit(2)
    qc.h(0)
    qc.cx(0,1)
    display(qc.draw('mpl'))
    qc_transpiled = transpile(qc, backend=backend, 
        optimization_level=3)
    display(qc_transpiled.draw('mpl'))
    # Display the circuit layout for the backend.
    display(plot_circuit_layout(qc_transpiled, backend, 
        view='physical'))
    

    电路布局稍微复杂一些,因为它不仅需要后端作为输入,你还必须提供你想要在其上运行的编译后的量子电路。

    例如,仍然在 ibmq_burlington 上,我们可能想要运行一个贝尔电路。

    前面的代码可能会得到以下结果:

Figure 5.8 – 具有两个量子比特的贝尔量子电路

图 5.8 – 具有两个量子比特的贝尔量子电路

翻译后的电路告诉我们,我们将在量子比特 0 和 1 上运行电路。因为我们开始时是一个 2 量子比特电路,所以我们给了编译器将任意两个量子比特分配给我们的电路的选项:

Figure 5.9 – 翻译后的贝尔电路

img/Figure_5.9_B14436.jpg

图 5.9 – 翻译后的贝尔电路

电路布局显示给我们预期的量子比特分配:

Figure 5.10 – 贝尔电路中的 CX 门从量子比特 0 映射到量子比特 1

img/Figure_5.10_B14436.jpg

图 5.10 – 贝尔电路中的 CX 门从量子比特 0 映射到量子比特 1

该视图以象征性的方式说明了物理芯片的外观,没有任何技术细节。

还有更多...

我们已经看到了 Qiskit® 中的可视化步骤。你也可以在 IBM Quantum Experience® 中获得相同的信息。

让我们看看:

  1. 登录 IBM Quantum Experience® quantum-computing.ibm.com

  2. 欢迎 页面的右侧,你会看到一个可用后端的列表:Figure 5.11 – IBM Quantum Experience® 主页

    图 5.11 – IBM Quantum Experience® 主页

  3. 点击你感兴趣的后端,例如 ibmq_burlington,以查看芯片布局和附加信息:Figure 5.12 – ibmq_burlington 芯片的细节

图 5.12 – ibmq_burlington 芯片的细节

这里有一个简短的数据列表,你可以看到所选芯片的不同部分:

  • 在线或离线:后端目前可用吗?

  • 当前队列:目前有多少人在使用后端?这个数字为你提供了一个粗略的估计,即后端有多忙,以及你的程序可能需要多长时间才能运行。

  • 提供商:这将是对免费后端开放的。

  • 实际量子芯片的蓝图视图:该视图以象征性的方式说明了物理芯片的外观,没有任何技术细节。

  • 连接图:量子比特之间的箭头说明了它们如何使用 2 量子比特门(如CNOT门,也称为受控非CX)进行连接。连接可以从箭头开始的量子比特到箭头结束的量子比特。

  • 单量子比特错误率:这是量子比特质量的衡量标准。它是量子比特本身的错误率和读出错误的总结。基本上,这是量子比特处于一个状态时被读取为另一个状态的概率。

  • CNOT 错误率:这是量子比特连接质量的衡量标准。它是两个纠缠量子比特读取相反纠缠的概率。

  • 量子比特数量:可用的量子比特数量。

  • 上线时间:机器上线的时间和日期。

  • 最大射击次数:你可以在后端上运行的最大射击次数。

  • 量子体积:后端的测量量子体积。量子体积是 IBM 为衡量今天量子计算机性能所提出的基准。

  • u1u2u3cxid。有了这些门,你可以构建 Qiskit®支持的所有其他量子门。

你可以使用可视化的 IBM Quantum®信息作为编写和执行你的量子程序的指导,并包括诸如量子比特属性和错误图等方面,以针对特定的后端微调你的程序。然而,在纯、非用户界面 Qiskit®环境中,你可能希望在不使用用户界面的情况下访问这些数据。所有这些内容都在下一个菜谱中介绍,我们将直接在 Qiskit®中挖掘这些数据。

参见

关于量子体积概念的概述和解释,请参阅 Medium 文章:量子体积究竟是什么?,作者 Ryan F. Mandelbaum,IBM Quantum®和 Qiskit®的高级技术作家:medium.com/qiskit/what-is-quantum-volume-anyway-a4dff801c36f

使用 Qiskit®探索选定的后端

可视化探索后端数据是一个方便但严格的手动过程。有时你可能想在运行程序时将后端信息包含在程序逻辑中,例如,选择合适的后端或动态地在最佳量子比特上应用你的门。为此,我们可以通过使用 Qiskit®直接从可用的后端信息中提取这些数据。

在这个菜谱中,我们将使用backend.configuration()backend.status()backend.properties()方法来检索和列出可用的和正在运行的后端,以及一些重要的配置数据,例如量子比特数量、你可以运行的最大实验数量以及队列中挂起的作业数量。我们还将挖掘一些重要的量子比特参数,例如选定的后端的 T1、T2、频率和读出错误。

好的,让我们看看它是如何完成的:

  1. 首先导入 IBMQ 类并加载我们的账户。

  2. 获取所有可用和运行中的后端。

  3. 挖掘并打印出用于比较的后端标准,例如名称、量子比特数量、最大实验次数和挂起的作业数量。

  4. 选择具有五个量子比特的最不繁忙的后端。

  5. 打印出选定后端的量子比特属性。

你现在可以更仔细地查看后端选定的属性,例如,使用这些信息来决定你想要在哪个后端上运行你的程序。

准备工作

下一个菜谱中所需文件可从以下链接下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter05/ch5_r5_explore.py

如何操作...

根据你编写的量子程序类型,后端的一些方面可能对你很重要,你可能希望在编写程序时直接包含这些方面。例如,你可能对具有最小门错误和读出错误的量子比特感兴趣,或者如果你正在运行深度电路,你可能对长的 T1 和 T2 时间感兴趣:

  1. 像往常一样,让我们首先导入 IBMQ 类并加载我们的账户:

    from qiskit import IBMQ
    from qiskit.providers.ibmq import least_busy
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 获取所有可用后端。

    我们将使用简单的 Python 脚本来根据不同的标准(名称、量子比特数量、每天允许的最大实验次数和挂起的作业数量)比较可用的后端。首先,我们需要一个可用后端的列表:

    available_backends = provider.backends(operational=True)
    
  3. 挖掘一些后端参数进行比较。

    要找到参数,我们遍历列表并打印出每个后端的四个选定参数 – namen_qubitsmax_experimentspending_jobs

    print("{0:20} {1:<10} {2:<10} {3:<10}".format("Name",
        "#Qubits","Max exp.","Pending jobs"))
    print("{0:20} {1:<10} {2:<10} {3:<10}".format("----","---
        ----","--------","------------"))
    for n in range(0, len(available_backends)):
        backend = provider.get_backend(str(
            available_backends[n]))
        print("{0:20} {1:<10} {2:<10} {3:<10}".
            format(backend.name(),
            backend.configuration().n_qubits,
                backend.configuration().
            max_experiments,backend.status().pending_jobs))
    

    上述代码可能给出以下结果:

    ![图 5.13 – 显示选定参数的可用后端]

    ](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_5.13_B14436.jpg)

    图 5.13 – 显示选定参数的可用后端

  4. 现在,我们可以深入了解并查看一些最不繁忙的后端(具有五个量子比特)的可用量子比特数据,例如 T1 和 T2 热退相干值、频率和量子比特的读出错误。

    对于这个练习,我们可以编写另一个简单的 Python for 循环来打印后端量子比特的属性,例如名称、值和相关的数据条目单位。

    我们将遍历后端的量子比特数量(least_busy_backend.configuration().n_qubits)以及每个量子比特的属性参数数量(len(least_busy_backend.properties().qubits[0])):

    least_busy_backend = least_busy(provider.backends(
        n_qubits=5,operational=True, simulator=False))
    print("\nQubit data for backend:",
        least_busy_backend.status().backend_name)
    for q in range (0,
        least_busy_backend.configuration().n_qubits):
        print("\nQubit",q,":")
        for n in range (0, len(least_busy_backend.
            properties().qubits[0])):
            print(least_busy_backend.properties().qubits[q]
                [n].name,"=",least_busy_backend.properties().
                qubits[q][n].value,
                least_busy_backend.properties()
                .qubits[q][n].unit)
    

    上述代码应给出类似于以下的结果:

图 5.14 – 选定后端的量子比特详细信息

图 5.14 – 选定后端的量子比特详细信息

这样,我们突然对量子比特有了更多的了解。它们不再是逻辑实体,而是具体的物理对象,尽管这些物理对象表现出量子行为。在第八章的“用 Ignis 清理您的量子行为”菜谱中,即[比较芯片上的量子比特],我们将探讨如何在您的程序中使用backend.properties().gates信息。

更多...

在这个菜谱中,我们查看了一组选定的后端和量子比特属性。使用 Qiskit®,您可以通过以下方法挖掘更多信息:

  • backend.configuration()

    backend_name

    backend_version

    n_qubits

    basis_gates

    gates

    local

    simulator

    conditional

    open_pulse

    memory

    max_shots

  • backend.status()

    backend_name

    backend_version

    operational

    pending_jobs

    status_msg

  • backend.properties()

    backend_name

    backend_version

    last_update_date

    qubits

    gates

    general

    小贴士

    要打印出每个方法的所有值的完整列表,请使用to_dict()参数。例如:backend.configuration().to_dict()

尝试修改示例代码以查找特定的参数,如下所示:

  • 后端名称

  • 后端可用的基本门

  • 指定单个量子比特如何通信的量子比特耦合图

  • 后端中门及其属性的列表

第六章:第六章:理解 Qiskit® 量子门库

在本章中,我们将探索 Qiskit® 提供的量子门。通过包含一个具有最常见门的量子门库,Qiskit® 使编写电路变得容易。

我们将要探讨的门中包括用于基本量子比特翻转的泡利 X、Y 和 Z 门,用于创建量子比特叠加的 H(或哈达玛)门,以及用于创建量子纠缠的 CX(控制非)门。为了快速复习,请查看 第四章从 Terra 的基础开始

我们还将探讨专门的 S 和 T 门,使用 R 门旋转我们的量子比特,然后展示如何仅使用最小的一组 U1、U2、U3、ID 和 CX 基础门来转换其他门,以便直接在量子计算机上使用。

我们将简要介绍多量子比特门,并最终以对 Qiskit® 程序中我们串出的简单门如何被编译器转换成更复杂的基门集的简要了解来结束我们的参观,这样我们才能在真实的量子计算机上运行它们。

在本章中,我们将涵盖以下内容:

  • 可视化量子门

  • 使用泡利 X、Y 和 Z 门进行翻转

  • 使用 H 门创建叠加态

  • 使用相位移位门 S、S†、T 和 T† 进行固定的 Z 旋转

  • 使用 Rx、Ry 和 Rz 在轴周围进行自由旋转

  • 使用基础门 – U1、U2、U3 和 ID 构建我们的电路

  • 在 2 个量子比特上使用门

  • 在超过 2 个量子比特上使用门

  • 你的量子电路真正看起来是什么样子

在这些菜谱中,你将接触到量子门单位矩阵和量子比特状态向量,这些我们在 第二章使用 Python 进行量子计算和量子比特中的“快速介绍量子门”菜谱中从数学上进行了讨论。如有需要,请随时返回并再次测试数学。

技术要求

本章中讨论的量子程序可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter06

可视化量子门

为了帮助我们理解量子门,我们可以使用 ch6_r1_quantum_gate_ui.py 示例程序。

这个菜谱与我们所看到的略有不同。到目前为止,我们主要只是在 Python 包装器内使用 Qiskit® 命令,没有进行任何实际的编码。这次,我们首先构建了一个基本的 Python 实现,以创建一个非常基本的 门探索前后界面。当你运行程序时,它会提示你选择一个初始量子比特状态和一个要应用于量子比特的门;然后它创建一个可视化来显示门对量子比特的作用。

脚本为您构建电路,然后显示支持门、状态向量和对应于门操作的布洛赫球或 Q 球可视化的基本最小电路。可视化突出显示了门之前量子比特的状态以及门之后状态的变化。

混合经典/量子程序

因此,我们在这里做的是构建一个混合经典/量子程序,其中我们使用 Python 来驱动用户输入控制、通用逻辑和展示,并使用 Qiskit®组件来访问量子相关功能。这就是我们在接下来的章节中将要做的事情。

准备工作

在我们进入可视化器之前,让我们花一点时间讨论一些基本的量子比特状态,我们可以用这些状态初始化我们的量子比特。你们很熟悉其中两个(),但为了理解我们的量子比特状态向量在布洛赫球上的指向,这里快速介绍一下其余的状态,包括它们的狄拉克矢量描述和布洛赫球参考:

  • :沿 z 轴直接向上

  • :沿 z 轴直接向下

  • :沿 + x 轴向外延伸

  • :沿 - x 轴向内延伸

  • :沿 + y 轴向右延伸

  • :沿 - y 轴向左延伸

图 6.1 – 布洛赫球上映射的初始量子比特状态

图 6.1 – 布洛赫球上映射的初始量子比特状态

在本食谱中,我们将探索几个有趣的 Qiskit®功能:

  • qiskit.visualization 类包含各种方法来可视化您的量子比特和电路。在本食谱中,我们将使用以下方法:

    布洛赫球:显示单个量子比特作为布洛赫球:

    plot_bloch_multivector(state_vector)
    

    Q 球:在球面上显示 1 个或多个量子比特作为状态概率向量:

    plot_state_qsphere(state_vector)
    
  • 初始化:用于将量子比特初始化到特定的初始状态:

    circuit.initialize(initial_vector,qubit)
    
  • 状态向量模拟器:一个 Aer 量子模拟器,用于计算量子比特的状态向量:

    Aer.get_backend('statevector_simulator')
    
  • 单元模拟器:一个 Aer 量子模拟器,允许您计算量子电路的单元矩阵。如果您在只包含单个门的电路上使用此模拟器,本质上可以提取该量子门的矩阵表示:

    Aer.get_backend('unitary_simulator')
    
  • QuantumCircuit.from_qasm_str(qasm_string) 方法,我们在第三章的“在两个世界之间移动”食谱中进行了探索,IBM Quantum® 经验 – 拖放量子计算

示例程序的代码在此处可用:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r1_quantum_qate_ui.py

如何做到这一点...

示例程序稍微有些复杂,但运行起来很简单。每个步骤都通过选择起始状态和门或简单的 Hit Enter(在下一步开始之前)之间的输入步骤来分隔:

  1. 在您的 Python 环境中运行 ch6_r1_quantum_gate_ui.py 示例文件。

    在第一次提示时,输入您第一个量子比特的起始状态。如果您选择使用超过 1 个量子比特的门,例如 CX 门,则您在此处选择的量子比特状态是用于控制量子比特的。您的选项如下:

    图 6.2 – 第一次提示:选择起始状态

    # List our start states
    start_states=["1","+","-","R","L","r","d"]
    valid_start=["0"]+start_states
    
  2. 在第二次提示时,输入您想要探索的门,例如 X图 6.3 – 第二次提示:选择门

    图片

    # List our gates
    rot_gates=["rx","ry","rz"]
    unitary_gates=["u1","u2","u3"]
    single_gates=["id","x","y","z","t","tdg","s","sdg","h"]
        +rot_gates
    oneq_gates=single_gates+unitary_gates
    control_gates=["cx","cy","cz","ch"]
    twoq_gates=control_gates+["swap"]
    all_gates=oneq_gates+twoq_gates+rot_gates+unitary_gates
    

    现在程序使用从空白电路上的示例代码中获取的 get unitary() 函数来检索并打印您所选门的单位矩阵。

    对于 X 门,输出将类似于以下内容:

    图 6.4 – 第一次输出:门的单位矩阵

    图 6.4 – 第一次输出:门的单位矩阵

    将此与我们在 第二章 中计算的 X 门的矩阵版本进行比较,使用 Python 进行量子计算和量子比特

    图片

    我们现在已准备好程序的精华部分。

  3. Enter 键以创建初始设置。

    程序调用 qgate(gate,start) 函数根据您的输入设置电路。它使用 create_circuit(n_qubits,start) 与门输入一起设置 1 个量子比特或 2 个量子比特的电路,然后使用 qgate_out(circuit,start) 函数显示空电路。在此阶段,电路将仅包括一个设置为所选起始状态的 initialized 量子比特,如果输入状态是 图片,则我们根本不需要初始化电路。

    initialize() 方法接受一个复数振幅向量和目标量子比特作为输入,并添加一个看起来很像门的电路指令。复数振幅必须是归一化的,在程序中,我们使用以下方式创建向量:[a * complex(1, 0), b * complex(1, 0)]

    输出应该类似于以下内容:

    图 6.5 – 第二次输出:初始电路、状态向量和 Bloch 球

    图片

    图 6.5 – 第二次输出:初始电路、状态向量和 Bloch 球

  4. 再次按 Enter 键以添加您所选的门并显示门应用后的最终结果。

    调用 qgate(gate,start) 函数的最终结果是返回完整的电路。我们现在使用 qgate_out(circuit,start) 函数来显示在应用门之后的最终结果。

    这是 X 门作用于初始化为 图片 的量子比特的预期输出:

图 6.6 – 第三次输出:门应用后的电路、状态向量和 Bloch 球

图 6.6 – 第三个输出:门后的电路、状态矢量和布洛赫球体

初始运行的结果是量子比特从 状态开始,指向正上方,然后通过 X 门翻转至 ,指向正下方。

它是如何工作的...

在程序中,我们定义了几个运行程序 Qiskit®特定功能的函数:

  • get_psi(circuit): 这是我们之前在第四章中介绍的函数,从 Terra 开始脚踏实地,略有变化。我们不是用它直接绘制布洛赫矢量,而是设置它返回状态矢量,这样我们就可以在其他地方使用它。该函数接受一个量子电路作为输入,并使用 Qiskit Aer 提供的statevector_simulator后端返回该电路的状态矢量:

    def get_psi(circuit):
        global psi
        backend = Aer.get_backend('statevector_simulator') 
        result = execute(circuit, backend).result()
        psi = result.get_statevector(circuit)
        return(psi)
    
  • get_unitary(circuit): 这个函数接受一个量子电路作为输入,并使用 Aer 后端中的unitary_simulator返回该电路的单位矩阵:

    def get_unitary(circuit):
        simulator = Aer.get_backend('unitary_simulator')
        result = execute(circuit, simulator).result()
        unitary = result.get_unitary(circuit)  
        return(unitary)  
    
  • create_circuit(n_qubits,start): 这个函数创建一个量子电路并将其初始化为所选的起始状态。

    我们首先根据用户输入设置起始向量:1+-RLrd

    def create_circuit(n_qubits,start):
        if start=="1":
            initial_vector = [0,complex(1,0)]
        elif start=="+":
            # Create |+> state
            initial_vector = [1/sqrt(2) * complex(1, 0),             1/sqrt(2) * complex(1, 0)]
        elif start=="-":
            # Create |-> state
            initial_vector = [1/sqrt(2) * complex(1, 0),             -1/sqrt(2) * complex(1, 0)]
        elif start=="R":
            # Create  state
            initial_vector = [1/sqrt(2) * complex(1, 0),            1*1.j/sqrt(2) * complex(1, 0)]
        elif start=="L":
            # Create  state
            initial_vector = [1/sqrt(2) * complex(1, 0),            -1*1.j/sqrt(2) * complex(1, 0)]
        elif start=="r":
            # Create random initial vector
            theta=random.random()*pi
            phi=random.random()*2*pi
            a = cos(theta/2)
            b = cmath.exp(phi*1j)*sin(theta/2)
            initial_vector = [a * complex(1, 0),            b * complex(1, 0)]
        elif start=="d":
            a = cos(start_theta/2)
            b = cmath.exp(start_phi*1j)*sin(start_theta/2)
            initial_vector = [a * complex(1, 0),            b * complex(1, 0)]
        else:
            initial_vector = [complex(1,0),0]
        if start!="n":
            print("\nInitial vector for |"+start+"\u232A:")
            print(np.around(initial_vector, decimals = 3))   
    

    然后,我们为指定的量子比特数量创建电路,并在起始状态不是 的情况下初始化量子比特:

        circuit = QuantumCircuit(n_qubits)
        if start in start_states:
            circuit.initialize(initial_vector,n_qubits-1)
        return(circuit)
    
  • qgate_out(circuit,start): 此外,我们还使用print功能与一些 Qiskit®方法一起创建和保存我们电路和量子比特的布洛赫球表示的图像:

    def qgate_out(circuit,start):
        # Print the circuit
        psi=get_psi(circuit)
        if start!="n":
            print("\nCircuit:")
            print("--------")
            print(circuit)
            print("\nState vector:")
            print("-------------")
            print(np.around(psi, decimals = 3))
            display(plot_bloch_multivector(psi))
            if circuit.num_qubits>1 and gate 
                in control_gates:
                display(plot_state_qsphere(psi))
        return(psi)
    
  • qgate(gate,start): 这个函数接受一个门和一个起始状态作为输入,然后从这些输入创建一个量子电路。对于单量子比特门,门被添加到第一个量子比特上,而对于双量子比特门,第二个量子比特是控制量子比特,第一个量子比特是被控制量子比特。

    然后它将输入门与oneq_gates列表进行比较,并立即调用create_circuit()函数来创建一个 1 量子比特或 2 量子比特电路。在此阶段,我们还创建了正确的 1 量子比特或 2 量子比特 QASM 字符串,我们使用from_qasm_str()方法将其附加到电路中,该方法我们在第三章在两个世界之间移动食谱中开始探讨,IBM Quantum Experience® – 量子拖放。幸运的是,我们门的 QASM 代码与门名一一对应。要添加x门,我们使用以下命令:

    circuit+=QuantumCircuit.from_qasm_str(qasm_string+gate+"    q[0];")
    

    字符串加法在from_qasm_string()命令中:

    qasm_string+gate+" q[0];" 
    

    这转化为以下代码,并将x门添加到电路中:

    OPENQASM 2.0; include "qelib1.inc"; 
    qreg q[1];
    x q[0];
    

    qgate()函数然后返回电路,我们可以继续:

    def qgate(gate,start): 
        # If the gates require angles, add those to the QASM
        # code
        qasm_angle_gates={"rx":"rx("+str(theta)+") q[0];",        "ry":"ry("+str(theta)+") q[0];",
            "rz":"rz("+str(phi)+") q[0];",        "u1":"u1("+str(phi)+") q[0];",        "u2":"u2("+str(phi)+",
            "+str(lam)+") q[0];", 
            "u3":"u3("+str(theta)+",
            "+str(phi)+","+str(lam)+") q[0];"}
        # Create the circuits and then add the gate using 
        # QASM import 
        if gate in oneq_gates:
            circuit=create_circuit(1,start)
            qasm_string='OPENQASM 2.0; include "qelib1.inc";
                qreg q[1];'
        else: 
            circuit=create_circuit(2,start)
            qasm_string='OPENQASM 2.0; include "qelib1.inc";            qreg q[2];'
        qgate_out(circuit,start)    
        if gate in oneq_gates:
            if gate in rot_gates+unitary_gates:
                circuit+=QuantumCircuit.from_qasm_str(
                    qasm_string+qasm_angle_gates[gate])
            else:
                circuit+=QuantumCircuit.from_qasm_str(
                    qasm_string+gate+" q[0];")
        else:
            circuit+=QuantumCircuit.from_qasm_str(
                qasm_string+gate+" q[1],q[0];")   
        return(circuit)
    

因此,现在您拥有了一个小程序,可以设置非常基本的单门电路,研究初始和最终状态向量,查看所选门的单位矩阵,并查看门如何使 Bloch 向量移动,并在Q-sphere上可视化,其中Bloch 球不再足够。

现在通过完成本章剩余的食谱来尝试这个程序,以探索 Qiskit®提供的量子门的基本集合。

参见

要快速直观地查看单量子比特的 Bloch 球表示以及某些门操作对其的影响,请查看 Qiskit 倡导者詹姆斯·韦弗的grok-bloch应用程序:github.com/JavaFXpert/grok-bloch。您可以从自己的 Python 环境中安装并运行它,或者在此处在线运行:javafxpert.github.io/grok-bloch/

使用 Pauli X、Y 和 Z 门翻转

Pauli X、Y 和 Z 门都作用于单个量子比特,并执行类似于经典 NOT 门的作用,翻转经典比特的值。例如,X 门将发送到,反之亦然。

正如我们将要看到的,X 门实际上是在弧度下围绕x轴的旋转。对于 Pauli Y 和 Z 门也是如此,但沿着yz轴相应地旋转。

从数学上讲,X、Y 和 Z 门可以用以下单位矩阵表示:

这个食谱将作为如何使用章节中提供的示例代码的模板。其余的食谱将主要概述更深入的细节。

让我们通过运行量子门 UI 示例程序来查看 Pauli X、Y 和 Z 门。程序首先设置一个简单的量子电路,其中包含一个从您选择的态开始的单一量子比特。然后,将选定的门添加到电路中,然后运行单位模拟器和状态向量模拟器,以显示以量子比特状态向量和门单位矩阵形式的结果。

示例脚本可在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r1_quantum_qate_ui.py

如何操作...

  1. 运行ch6_r1_quantum_gate_ui.py示例程序。

  2. 选择您的量子比特的起始状态。

    X 和 Y 门与配合良好,本质上充当 NOT 门。

    )的情况下,X 门将不执行任何操作,但 Y 和 Z 门将创建一个的相移,创建

    还要测试公式 6.3.8公式 6.3.9以查看它们的作用。如果您使用r随机)或d定义)输入,那么您可以更详细地探索门旋转。

  3. 当提示时,输入xyz以选择要测试的门。

    我们在可视化量子门菜谱中测试了 X 门。现在来看看其他的。

    随机起始状态的 Z 门可能会产生以下输出:

图 6.7 – 随机起始状态的 Z 门

图 6.7 – 随机起始状态的 Z 门

更多...

Pauli 门不能直接在量子后端上运行,但在你运行程序之前会自动转换为以下单位基门:

x(qubit) = u3(3.141,0,3.141,qubit)
y(qubit) = u3(3.141,1.571,1.571,qubit)
z(qubit) = u1(3.141,qubit)

更多信息,请参阅本章中的使用基本门构建我们的电路 – U1、U2、U3 和 ID你的量子电路真正看起来是什么样子菜谱。

使用 H 门创建叠加

现在,让我们回顾一下我们的老朋友第四章从 Terra 的起点开始HadamardH门。这是一个相当专业的门,我们可以用它来创建一个通用的量子比特叠加。但不仅如此;我们还可以利用 H 门来改变测量轴,从通用的z(或计算)轴变为x轴,以获得对量子比特行为的更深入了解。更多内容请参阅更多内容部分。

H 门可以用以下单位矩阵表示:

公式 6.4.0

除非你非常擅长解释矩阵运算,否则这个门会对你的量子比特做什么可能并不完全清楚。如果我们将其描述为两个量子比特旋转的组合,事情可能会变得清晰起来。当你对你的量子比特应用 Hadamard 门时,你让它经历两个旋转:首先是一个围绕y轴的公式 6.4.1旋转,然后是一个围绕x轴的公式 6.4.2旋转。

对于处于状态公式 6.4.3的量子比特,这意味着我们从一个北极开始,然后下降到赤道,最终到达布洛赫球上的公式 6.4.4位置,最后围绕x轴旋转。同样,如果您从南极公式 6.4.5开始,您首先上升到赤道,但在x轴的另一端,最终到达公式 6.4.6

如果我们对公式 6.4.7进行矩阵运算,我们得到以下结果:

公式 6.4.8

现在我们可以使用以下狄拉克矢量表示法:

公式 6.4.9

公式 6.5.0

如果我们将ab替换为上面的公式 6.5.1,我们得到公式 6.5.2公式 6.5.3,这对应于公式 6.4.4

如果我们将哈达玛门应用于处于纯 以外的量子比特状态,我们将量子比特旋转到新的位置。

示例脚本可在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r1_quantum_qate_ui.py

如何操作...

要探索哈达玛门,我们运行了在 可视化量子门 食谱中描述的量子门程序:

  1. 运行 ch6_r1_quantum_gate_ui.py 示例程序。

  2. 为你的量子比特选择一个起始状态。

    H 门与 配合良好,将你置于赤道上的等概率叠加状态。使用 ,H 门将等概率叠加返回到相应的计算状态,。尝试使用 、随机或定义输入来更详细地探索门旋转。

  3. 当提示时,输入 h 选择要测试的门。将 H 门应用于 将产生以下输出:

![图 6.8 – 以 |0> 为起始状态的 H 门]

图片

图 6.8 – 以 |0> 为起始状态的 H 门

还有更多...

哈达玛门不能直接在量子后端运行,但在你运行程序之前,会自动编译成以下单位算子基门:

h(qubit)=u2(0,3.141,qubit)

如需更多信息,请参阅本章中的 使用基本门构建我们的电路 – U1、U2、U3 和 ID 以及 你的量子电路真正看起来是什么样子 两个食谱。

哈达玛门通常也用于将你的测量轴从默认(计算)z 轴更改为 x 轴。通过沿 x 轴测量,你可以检测量子比特的相位。

为了确定相位的值,你必须进行一次额外的测量,这次是沿 y 轴。为此,你使用哈达玛门与 S dagger 门结合,以将量子比特移动到 Y 轴。

你可以通过想象我们将量子比特旋转以匹配我们想要测量的轴,然后进行标准的 Z 轴测量来可视化测量基的变化。当我们沿 x 轴测量时,我们将量子比特旋转到面向 方向,而对于 y 轴,则指向 方向。

这里是需要应用于测量三个玻尔球体轴的门的组合:

沿 z 轴测量(计算基):

图 6.9 - 沿 z 轴测量

图 6.9 - 沿 z 轴测量

沿 x 轴测量:

图 6.10 - 沿 x 轴测量

图 6.10 - 沿 x 轴测量

沿 y 轴测量:

图 6.11 - 沿 y 轴测量

图 6.11 - 沿 y 轴测量

参见

有关沿不同轴测量量子比特相位的示例,请参阅 第九章探索量子相位回冲 菜谱 [第九章Grover 的搜索算法]。

固定 z 轴旋转与相位移位门 S、![img/Formula_06_0681.png]、T 和 ![img/Formula_06_0691.png]

S、![img/Formula_06_070.png]、T 和 ![img/Formula_06_071.png] 门都在量子比特的 z 轴周围进行旋转。这意味着当你测量量子比特时,测量结果为 1 或 0 的概率不会发生变化。发生变化的是量子比特的相位。

S 门和 T 门不是它们自己的逆

由于 S 门和 T 门在 z 轴周围进行固定旋转,它们是不可逆的。连续添加两个这样的门不会取消它们。相反,Qiskit 包含 S† 和 T† 门,它们作为逆 S 和 T 门。为了快速回顾,请参阅 第二章使用 Python 进行量子计算和量子比特 中的 快速介绍量子门 菜谱。

从数学上讲,S 门和 T 门可以表示为以下单位矩阵:

![img/Formula_06_072.png] ![img/Formula_06_073.png] ![img/Formula_06_074.png] ![img/Formula_06_075.png]

示例脚本可在以下位置获取:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r1_quantum_qate_ui.py

如何做到...

要探索相位移位门,我们运行了在 可视化量子门 菜谱中描述的量子门 UI 程序:

  1. 运行 ch6_r1_quantum_gate_ui.py 示例程序。

  2. 为您的量子比特选择一个起始状态。

    由于这些相位移位门在 z 轴周围进行旋转,因此使用默认的 ![img/Formula_06_0221.png] 或 ![img/Formula_06_023.png] 量子比特几乎没有意义,这些量子比特是无相的。相反,选择一个叠加态量子比特,例如 +-LRrd

  3. 当提示时,输入 ssdgttdg 以选择要测试的门。![img/Formula_06_078.png] 上的 S 门应产生以下输出:图 6.12 – 具有起始状态的 S 门

图 6.12 – 具有起始状态 ![img/Formula_06_079.png] 的 S 门

更多...

相位移位门不能直接在量子后端上运行,但在你运行程序之前会自动编译成以下单位基门:

s(qubit) = u1(1.570,qubit)
sdg(qubit) = u1(-1.570,qubit)
t(qubit) = u1(0.785,qubit)
tdg(qubit) = u1(-0.785,qubit)

更多信息,请参阅本章的 使用基本门构建我们的电路 – U1、U2、U3 和 ID你的量子电路真正看起来是什么样子 菜谱。

在轴周围进行自由旋转的 Rx、Ry 和 Rz

虽然所有相移门都围绕 z 轴旋转以改变量子比特的相位,但旋转门在布洛赫球的相应轴周围进行旋转。Rx 和 Ry 在相位角 () 0 和 处旋转量子比特 角度,而 Rz 围绕 Z 旋转,特殊情况为 ,对应于 S 和 T 门。

R 门不是它们自己的逆

由于 R 门在 X、Y 或 Z 轴周围执行自由旋转,它们是不可逆的。连续添加两个这样的门不会抵消它们。为了快速回顾,请参阅第二章中的快速介绍量子门菜谱,使用 Python 进行量子计算和量子比特

从数学上讲,R 门可以用单位矩阵表示:

样本脚本可在以下位置获取:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r1_quantum_qate_ui.py

如何做到...

要探索相移门,我们运行了在可视化量子门菜谱中描述的量子门程序:

  1. 运行 ch6_r1_quantum_gate_ui.py 样本程序。

  2. 为你的量子比特选择一个起始状态。

    R 门在相应的轴周围执行任意旋转。

    由于所有这些门都在相应的轴周围进行旋转,请玩转 以获得对旋转位置的感觉。然后使用随机或定义来测试,以探索更多奇特的旋转。

  3. 当提示时,输入 rxryrz 以选择要测试的门。

  4. 输入旋转的角度 ()。设置为 旋转的 Rx 门在 上,应该产生以下输出:图 6.13 – Rx 门以起始状态旋转

图 6.13 – Rx 门以 角度与 起始状态旋转

现在,再次使用不同的旋转角度进行测试,通过输入以下值来观察 和/或 角度的行为:

更多内容...

保罗门不能直接在量子后端上运行,但在你运行程序之前会自动编译成以下单位基门:

rx(,qubit) = u3(,-1.507,1.507,qubit)
ry(,qubit) = u3(,0,0,qubit)
rz(,qubit) = u1(,qubit)

更多信息,请参阅本章中的使用基门构建我们的电路 – U1、U2、U3 和 ID你的量子电路真正看起来是什么样子菜谱。

使用基门构建我们的电路 – U1、U2、U3 和 ID

让我们从探索我们在 第五章 中认识的三个门开始,使用 Qiskit® 探索 IBM Quantum® 硬件。你不会在你的量子程序中使用这三个基础门,U1、U2 和 U3,但是当你在量子计算机上运行电路时,它们作为所有其他单量子比特门的构建块。实际上,每个其他单量子比特门都可以只用 U3 门来表示。没有明确阻止你使用它们,但我们在后续食谱中将要介绍的门集合涵盖了我们需要的所有内容。

U 门不是它们自己的逆

由于 U 门在 xyz 轴周围进行自由旋转,它们通常是不可逆的。连续添加两个这样的门不会取消它们,除非旋转的总和是完整的旋转。为了快速回顾,请参阅 第二章 中的 A quick introduction to quantum gates 食谱,使用 Python 进行量子计算和量子比特

如果你还记得我们在 第五章 中探索 IBM Quantum® 后端时的情况,使用 Qiskit® 探索 IBM Quantum® 硬件,我们使用以下命令查看硬件后端可用的所谓 基础门

backend.configuration().basis_gates

返回的结果如下:

['u1', 'u2', 'u3', 'cx', 'id']

CX 和 ID 门是你在程序中使用的普通门,UX(受控非)用于创建纠缠,ID 用于运行一个不改变量子比特状态的门(关于这些内容稍后会有更多介绍)。U1、U2 和 U3 门则有所不同。

记得从 第二章 中,使用 Python 进行量子计算和量子比特,我们如何将量子门视为在 角度周围的旋转。这正是 U 门所做的事情,分别有一个、两个和三个输入。实际上,单量子比特 U 门旋转和 CNOT 门构成了量子计算的通用门集。在这里,Id 门是旋转的特殊情况,完全没有旋转。

基础门的关键在于,你可以直接在硬件上使用它们来编程你的量子比特。所有其他门都会被转换为基础门,然后按照你将在讨论编译器时看到的方式执行。

U3 门

U3 门实际上是量子门的瑞士军刀。它是量子比特操作的基单位矩阵。可以完成的每个单量子比特操作都可以使用 U3 门来完成,它可以表示为以下单位矩阵:

三个角度如下:

  • = 与状态向量之间的极角

  • = 从 x 轴的纵向角度 ()

  • = 量子比特的总相位角(在 Bloch 球上不可见)

U2 门和 U1 门基本上是 U3 门的专业化,就像其他量子门是 U 门的一般专业化一样。

U2 门

使用 U2 门,您可以同时操作两个角度。U2 门与具有的 U3 门相同。

U2 门可以用以下单位矩阵表示:

U1 门

使用 U1 门,您可以在z轴周围旋转量子比特的相位。U1 的一个特殊情况是 Rz 门,它具有相同的输入。U1 门与具有的 U3 门相同。

U1 门可以用以下单位矩阵表示:

使用这三个门,您可以执行所有可能的单量子比特操作。它们在阅读上并不那么用户友好,因此 Qiskit®包括所有相关量子门的翻译,以方便您的编程。然而,当您运行电路时,所有门都被翻译成所选后端支持的基门集合。

Pauli Id 门是一种特殊的门,它使量子比特保持其原始状态。该门可以用以下矩阵表示:

= .

准备工作

示例程序的代码在此处可用:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r2_u_animation.py.

在这个菜谱中,我们使用 Pillow 包来创建、保存和合并图像。有关 Pillow 的更多信息,请参阅pypi.org/project/Pillow/.

安装 Pillow

如果您需要在环境中安装 Pillow,可以使用以下命令:

(环境名称) … $ pip install --upgrade pip

(环境名称) … $ pip install --upgrade Pillow

如何操作...

在这个菜谱中,我们将可视化 U 门在 Bloch 球上的旋转。如您所知,我们可以使用plot_bloch_multivector()plot_state_qsphere()方法来可视化我们的状态矢量的行为以及可能的输出。这两个方法都提供了在某一时刻量子比特和特定角度集的 U 门的静态视图。

在示例程序中,您输入 U 门的角输入,然后程序在 0 和给定分辨率的角度之间进行快照,并生成一个动画 GIF,显示量子比特状态矢量在 Bloch 球上的移动。

注意,这个动画并没有真正展示您应用门时量子比特矢量的移动方式,而是展示了如何使用 U 门将量子比特状态矢量定位到您想要的位置:

  1. 首先,在你的 Python 环境中运行 ch6_r2_u_animation.py 脚本。

  2. 在提示下,输入你想要测试的门类型,然后输入该门所需的输入角度。对于 U3 门,你的输入可能如下所示,对于

    Animating the U gates
    ---------------------
    Enter u3, u2, or u3:
    u3
    Enter :
    1.57
    Enter :
    3.14
    Enter :
    0
    Building animation...
    

    在后台,我们现在调用 create_images() 函数,该函数接收你提供的输入,然后迭代创建一系列电路,这些电路迭代应用你选择的 U 门,输入角度被分成更小的角度,这由 steps 参数决定。然后,每个电路通过我们在第一个菜谱“可视化量子门”中创建的 get_psi() 函数中的状态向量模拟器运行,并最终使用 plot_bloch_multivector()plot_state_qsphere() 将其保存为 Bloch 球图像和 Q 球图像。这些图像持续追加到两个列表中,稍后将合并成动画。

    这就是我们构建函数的方法。

    首先,设置输入参数和所有内部函数变量:

    def create_images(gate,theta=0.0,phi=0.0,lam=0.0):
        steps=20.0
        theta_steps=theta/steps
        phi_steps=phi/steps
        lam_steps=lam/steps
        n, theta,phi,lam=0,0.0,0.0,0.0
    

    然后创建图像和动画工具:

        global q_images, b_images, q_filename, b_filename
        b_images=[]
        q_images=[]
        b_filename="animated_qubit"
        q_filename="animated_qsphere"
    

    最后,根据输入参数运行图像创建循环:

        while n < steps+1:
            qc=QuantumCircuit(1)
            if gate=="u3":
                qc.u3(theta,phi,lam,0)
                title="U3: \u03B8 = "+str(round(theta,2))+"                \u03D5 = "+str(round(phi,2))+" \u03BB =                "+str(round(lam,2))
            elif gate=="u2":
                qc.u2(phi,lam,0)
                title="U2: \u03D5 = "+str(round(phi,2))+"                 \u03BB = "+str(round(lam,2))
            else:
                qc.h(0)
                qc.u1(phi,0)
                title="U1: \u03D5 = "+str(round(phi,2))
            # Get the statevector of the qubit 
            # Create Bloch sphere images
            plot_bloch_multivector(get_psi(qc),title).            savefig('images/bloch'+str(n)+'.png')
            imb = Image.open('images/bloch'+str(n)+'.png')
            b_images.append(imb)
            # Create Q-sphere images
            plot_state_qsphere(psi).savefig(
               'images/qsphere'+str(n)+'.png')
            imq = Image.open('images/qsphere'+str(n)+'.png')
            q_images.append(imq)
            # Rev our loop
            n+=1
            theta+=theta_steps
            phi+=phi_steps
            lam+=lam_steps
    
  3. 创建并保存动画 GIF。

    最后一步是创建 GIF。在这里,我们使用 save_gif(gate) 函数迭代我们创建的图像列表,让 Pillow 使用我们的初始参数构建 GIF。这是我们所做的:

    def save_gif(gate):
        duration=100
        b_images[0].save(gate+'_'+b_filename+'.gif',
                   save_all=True,
                   append_images=b_images[1:],
                   duration=duration,
                   loop=0)
        q_images[0].save(gate+'_'+q_filename+'.gif',
                   save_all=True,
                   append_images=q_images[1:],
                   duration=duration,
                   loop=0)
        print("Bloch sphere animation saved as: \n"+os.        getcwd()+"/"+gate+"_"+b_filename+".        gif"+"\nQsphere animation saved as: \n"+os.        getcwd()+"/"+gate+"_"+q_filename+".gif")
    

    最终的 Python 输出可能如下所示:

    Bloch sphere animation saved as: 
    /<path_to_your_directory>/ch6/Recipes/u3_animated_qubit.gif
    Qsphere animation saved as: 
    /<path_to_your_directory>/ch6/Recipes/u3_animated_qsphere.gif
    
  4. 最终结果将是两个保存在你运行脚本相同目录下的动画 GIF。

    现在,你可以在图像查看器或网页浏览器中打开 GIF,查看一下:

    图 6.14 – 动画 GIF 的静态图像,展示 Bloch 球和 Q 球

图 6.14 – 动画 GIF 的静态图像,展示 Bloch 球和 Q 球

如果你并排同步查看动画 GIF,Q 球表示法将显示在 U 门应用于其上的量子比特的相对概率和相位角。尝试不同的角度和不同的循环参数,看看你如何使动画为你所用。

如果你将角度设置在 0 到 的范围内,并将你的步骤设置为合理的较高数值,你可以创建出漂亮的平滑循环效果。尽管如此,你完全可以设置非常高的角度,这将导致动画中出现相当混乱的行为。祝您玩得开心!

当你使用脚本测试 U 门时,请记住以下几点:

  • U3 允许你使用两个角度,,将量子比特向量指向 Bloch 球上的任何位置。第三个角度,,对 Bloch 球表示没有影响。

  • U2 与 U3 相同,但将 设置为 ,这会将量子比特向量放置在赤道上。

  • U1 与 U3 相同,但将 都设置为 0。U1 的一个特殊情况是 Rz。

更多内容...

嗯,在这种情况下,是少而不是多。本菜谱中的示例脚本将在你的本地环境中工作,但在你的 IBM Quantum Experience® 笔记本环境中效果不佳,因为你无法访问底层文件系统。

在 2 个量子位上使用门

双量子位门,如控制门,与普通单量子位门略有不同;它们允许你在量子位之间创建交互。一般来说,这相当于使用一个量子位作为控制量子位,另一个作为作用量子位。从数学上来说,这并不特别复杂,但直观上你可能需要思考一两次才能理解正在发生的事情。

准备中

我们将要接触的第一个双量子位门是我们在 第四章 中看到的控制非门,从 Terra 的基态开始。CX 门通常用于在控制量子位处于叠加态时在量子位之间创建纠缠。

当控制量子位是第二个量子位,受控量子位是第一个量子位时,CX 门可以表示为以下矩阵:

img/Formula_06_131.jpg

这对应于以下电路:

![图 6.15 – CX 门从 q_1 到 q_0]

图 6.15 – 从 q_1 到 q_0 的 CX 门

图 6.15 – 从 q_1 到 q_0 的 CX 门

解释这个方法的方式是运行第一个或控制量子位通过一个 Id 门,使其保持不变。第二个,或受控,量子位如果第一个量子位是 1,则应用一个 X 门。

这里有两个矩阵计算来演示:

img/Formula_06_132.jpg

在这里,第一个量子位是 1,第二个是 0。结果是两个量子位都是 1:

img/Formula_06_133.jpg

在这里,两个量子位都是 1。结果是第一个量子位是 1,第二个是 0。

CX 门是 IBM Quantum® 后端的基本门之一。

其他 CX 矩阵

如果 CX 门的方向相反,第一个量子位作为控制量子位,矩阵将看起来像这样:

img/Formula_06_134.png

为了让你确信,请进行计算。为了快速复习,请参阅 第二章 中的 快速量子门简介 菜谱,使用 Python 进行量子计算和量子位

样本脚本可在以下网址获取:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r1_quantum_qate_ui.py.

如何操作...

之前,对于单量子位门,量子门程序设置了一个只有一个量子位处于基态的简单量子电路 img/Formula_06_0221.png。当我们开始使用多量子位门时,程序将以两个量子位都处于 img/Formula_06_0221.png 的状态初始化。然后,选定的门将在电路中运行,并以量子位状态向量和门单位矩阵的形式显示结果。

要探索控制门,我们运行了可视化量子门配方中描述的量子门程序:

  1. 运行ch6_r1_quantum_gate_ui.py示例程序。

  2. 为你的控制量子比特选择一个起始状态。

    Cn 门使用单个量子比特作为控制量子比特,其他量子比特作为受控量子比特。如果控制量子比特的状态不是公式 _06_0221.png,则会在受控量子比特上执行一些操作。

    如果你想的话,可以先使用公式 _06_0221.png进行测试——只是为了验证没有执行任何操作,并且 2 量子比特的状态没有改变。然后使用起始状态公式 _06_139.png来创建贝尔态,就像我们在第四章中做的那样,从 Terra 的起点开始。完成这些后,尝试使用公式 _06_140.png、随机或定义来探索其他控制状态。

  3. 当提示时,输入cxcyczch以选择要测试的门。例如,控制量子比特状态公式 _06_078.png的 CX 门将生成以下输出:![图 6.16 – CX 门与起始状态|+> 图 6.16_B14436.jpg

图 6.16 – CX 门与起始状态|+>

在这里,请注意,在应用 CX 门之后,Bloch 球表示没有意义。量子比特现在是纠缠的,我们无法从它们那里获取更多的单个信息。在这种情况下,我们显示 Q 球解释以供说明,其中初始 Q 球表示得到状态公式 _06_142.png公式 _06_143.png的概率相等,在 X 门之后,得到公式 _06_144.png的概率相等,这是贝尔态所预期的。

它是如何工作的...

CX 门只是许多可能的二量子比特控制门之一。在 Qiskit®中内置的其他门还包括 CY 和 CZ 门:

公式 _06_145.jpg公式 _06_146.jpg

或者为什么不试试受控 Hadamard 门?

公式 _06_147.jpg

另一个实用的 2 量子比特门是 SWAP 门,它交换第一个和第二个量子比特的值。在这种情况下,没有纠缠,单个量子比特保持独立:

公式 _06_148.jpg

更多...

双量子比特门也可以写成基门组合的形式。例如,这是如何编码 CY 门的:

qc.u1(-1.507,0)
qc.cx(1,0)
qc.u1(1.507,0)

更多信息和测试转换其他 Cn 门,请参阅你的量子电路真正看起来是什么样子配方。

参见

《量子计算与量子信息》,第 10 周年纪念版,迈克尔·A·尼尔森与艾萨克·L·丘恩,第 4.3 节受控操作

使用超过 2 个量子比特的门

除了单量子比特和双量子比特门之外,Qiskit® 还支持 3 个和更多量子比特的门。当我们构建第九章 “Grover 搜索算法” 中的 3 量子比特 Grover 搜索算法电路时,我们将使用其中之一,即 Toffoli 门。为了完整性,我们将包括 Fredkin 门,但不会在其他示例中使用它;请随意尝试。

本食谱中的多量子比特门分别使用 2、更多和 1 个控制量子比特:

  • Toffoli:受控受控非(CCX),它接受 2 个量子比特作为输入,如果两个控制量子比特都被设置,则翻转第三个量子比特。

  • MCX:多控制非接受多个量子比特(控制)作为输入,如果所有量子比特都被设置,则翻转受控量子比特。

    (原则上)你使用的控制量子比特的数量没有限制。在 4 个或更多量子比特的 Grover 搜索算法电路中 第九章,“Grover 搜索算法”,我们通过使用 MCX 门构建了一个 4 量子比特的 CCCX 受控受控受控 门。

  • Fredkin:受控交换,(CSWAP),它接受单个量子比特作为输入,如果控制量子比特被设置,则交换其他两个量子比特。

如何做到这一点...

在你的量子程序中,使用以下示例代码来实现这些 3 量子比特门。

Toffoli

Toffoli 或 CCX 门由以下单位矩阵表示,其中第二个和第三个量子比特控制第一个量子比特:

以下代码实现了 CCX 门:

from qiskit import QuantumCircuit
qc=QuantumCircuit(3)
qc.ccx(2,1,0)
print(qc)

电路看起来是这样的:

图 6.17 – Toffoli 门

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_6.17_B14436.jpg)

图 6.17 – Toffoli 门

其他 CCX 矩阵

就像 CX 门一样,矩阵将根据你应该使用哪些量子比特来控制而有所不同。例如,这里是一个 CCX 门,其中第一个和第二个量子比特控制第三个量子比特的矩阵:

为了说服自己,做一下计算。

MCX

MCX 门用于构建具有一个以上控制门的通用受控非门。Toffoli(CCX)门是一个特殊例子,其中这是一个已经编码的门。对于具有超过 2 个控制量子比特的受控非门,你可以使用 MCX 门。

以下代码实现了 MCX 门作为 CCCX 门,其中第二个、第三个和第四个量子比特控制第一个量子比特:

from qiskit import QuantumCircuit
qc=QuantumCircuit(4)
qc.mcx([1,2,3],0)
print(qc)

电路看起来是这样的:

图 6.18 – 3 个控制量子比特的 MCX 门(CCCX)

图 6.18 – 3 个控制量子比特的 MCX 门(CCCX)

Fredkin

Fredkin 或 CSWAP 门由以下单位矩阵表示,其中控制第三个量子比特交换第一个和第二个量子比特:

以下代码实现了 CSWAP 门:

from qiskit import QuantumCircuit
qc=QuantumCircuit(3)
qc.cswap(2,1,0)
print(qc)

电路看起来是这样的:

图 6.19 – Fredkin 门

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_6.19_B14436.jpg)

图 6.19 – Fredkin 门

还有更多...

就像我们在本章中查看的其他门一样,Toffoli 和 Fredkin 门包含在 Qiskit® 的基本门集中。然而,它们不是基本门,这意味着它们需要使用一组基本门(如 u1u2u3idcx)重写。这被称为 转换,这是我们将在下一道菜谱“你的量子电路真正看起来是什么样子”中要做的。但在这里我们只是稍微提前了一点,展示了构建这些看似简单的门的复杂性。请尝试在下一道菜谱中尝试它们。

由基本门构建的 Toffoli

Toffoli 门可以在 Qiskit® 中作为一个单独的门输入,这让你相信门的执行将对应于 单个时间步,就像其他门一样:

图 6.20 – 由 10+ 个基本门构建的单个 Toffoli 门

图 6.20 – 由 10+ 个基本门构建的单个 Toffoli 门

当我们这样解开 CCX 时,我们看到事实略逊于仅仅一个时间步;单个门被转换成电路深度为 11。

由基本门构建的 Fredkin

与 Toffoli 门类似,当在硬件后端执行时,Fredkin 可能会被转换成大量基本门:

图 6.21 – 由 20+ 个基本门构建的单个 Fredkin 门

图 6.21 – 由 20+ 个基本门构建的单个 Fredkin 门

对于单个 Fredkin 门,电路深度从 1 到 22。在量子计算机上并非所有事情都容易做到。

你的量子电路真正看起来是什么样子

第三章IBM Quantum Experience® – Quantum Drag and Drop第五章使用 Qiskit® 探索 IBM Quantum® 硬件,我们提到了 u1u2u3idcx 的概念。我们在本章前面的菜谱中讨论了这些门,甚至列出了其他门如何作为这些门的实现或组合。

在这道菜谱中,我们将探讨电路转换的其他一些方面,例如以下内容:

  • 将常用门简单地转换为基本门

  • 为模拟器进行转换

  • 如果你的电路与后端物理布局不匹配,则进行电路转换

    单量子比特后端基本门

    大多数 IBM Quantum® 后端都有以下基本门:u1u2u3idcx。单量子比特后端(如 ibmq_armonk)不需要多量子比特门(如 CX),它不包括在内。如果你将后端设置为 ibmq_armonk 并运行以下命令,你将得到相应的输出:>>> backend.configuration().basis_gates Out: ['id', 'u1', 'u2', 'u3']

当我们在 IBM 量子计算机上执行量子程序时,我们的代码首先被转换成我们可以在硬件上直接执行的核基本门(u1u2u3idcx)。

准备工作

下面的食谱中所需的文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter06/ch6_r3_transpiler.py

这就是我们构建 Python 示例的方法:

  1. 首先,我们导入所需的类和方法,包括transpile

    from qiskit import QuantumCircuit, IBMQ
    from qiskit.compiler import transpile
    from qiskit.providers.ibmq import least_busy
    

    我们还加载了我们的账户(如果需要),并使用5个量子比特设置了一个后端:

    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    backend = least_busy(provider.backends(n_qubits=5,    operational=True, simulator=False)) 
    
  2. 让我们看看所选后端的基本门和耦合图。耦合图指定了双量子比特电路可能的量子比特连接:

    print("Basis gates for:", backend)
    print(backend.configuration().basis_gates)
    print("Coupling map for:", backend)
    print(backend.configuration().coupling_map)
    

    上述代码应该产生类似于以下输出的结果:

    ![Figure 6.22 – 后端的基本门和 CX 耦合图 img/Figure_6.22_B14436.jpg

    图 6.22 – 后端的基本门和 CX 耦合图

    对于单量子比特门的转换,耦合图并不重要。当我们编写使用双量子比特门的量子程序时,情况就不同了。

  3. 然后,我们设置build_circuit()函数来创建一个基本电路,并根据我们想要探索的电路选择性地添加门:

    def build_circuit(choice):
        # Create the circuit 
        qc = QuantumCircuit(5,5)
    
        if choice=="1":
            # Simple X
            qc.x(0)
        elif choice=="2":
            # H + Barrier
            #'''
            qc.x(0)
            qc.barrier(0)
            qc.h(0)
        elif choice=="3":
            # Controlled Y (CY)
            qc.cy(0,1)
        elif choice=="4":    
            # Non-conforming CX
            qc.cx(0,4)
        else:
            # Multi qubit circuit
            qc.h(0)
            qc.h(3)
            qc.cx(0,4)
            qc.cswap(3,1,2)
        # Show measurement targets
        #qc.barrier([0,1,2,3,4])
        #qc.measure([0,1,2,3,4],[0,1,2,3,4])
        return(qc)
    
  4. 最后,我们设置了main()函数来运行电路。

    主函数提示输入要测试的电路,调用build_circuit()函数,使用transpile()类转换返回的电路,然后显示结果:

    def main(): 
        choice="1"
        while choice !="0": 
            choice=input("Pick a circuit: 1\. Simple, 
                2\. H + Barrier, 3\. Controlled-Y, 
                4\. Non-conforming CX, 5\. Multi\n")
            qc=build_circuit(choice) 
            trans_qc = transpile(qc, backend)
            print("Circuit:")
            display(qc.draw())
            print("Transpiled circuit:")
            display(trans_qc.draw())
            print("Circuit depth:")
            print("---------------")
            print("Circuit:", qc.depth())
            print("Transpiled circuit:", trans_qc.depth())
            print("\nCircuit size:")
            print("---------------")
            print("Circuit:", qc.size())
            print("Transpiled circuit:", trans_qc.size())
    if __name__ == '__main__':
        main()
    

如何做到这一点...

让我们构建并转换一个简单的 X 电路:

  1. 在你的 Python 环境中运行ch6_r3_transpiler.py

  2. 当提示时,输入1以选择Simple X电路:![Figure 6.23 – 首先,选择 Simple X 电路 img/Figure_6.23_B14436.jpg

    图 6.23 – 首先,选择 Simple X 电路

  3. 这段代码应该产生类似于以下输出的结果:Figure 6.24 – 简单的单 X 门电路的输出

    图 6.24 – 简单的单 X 门电路的输出

    结果电路看起来相当相似。唯一真正的变化是 X 门现在是一个 U3 门,即基本门之一。

  4. 我们还可以通过提取电路及其转换电路的深度和大小来获得数值结果。

    这段代码应该产生以下电路深度和大小:

Figure 6.25 – 电路深度和大小

图 6.25 – 电路深度和大小

在这里,深度指的是量子计算机必须执行的端到端步骤的数量。每一步可能包括一个或多个门,具体取决于电路的布局。大小只是执行的总门数。

对于这个非常简单的电路转换,变化不大。U3 门执行围绕x轴所需的旋转,电路的深度和大小保持不变。技术上,你可以在代码中使用 U3 门而不是 X 门,但这样会不太透明。

为模拟器转换?

那么,如果你尝试为模拟后端转换你的电路会发生什么?实际上,模拟器包含的基门比硬件后端要多得多,因此转换会有所不同。为了测试这一点,只需在脚本中取消以下行的注释,将后端设置为模拟器,然后再次运行程序:

backend = provider.get_backend('ibmq_qasm_simulator')

ibmq_qasm_simulator 支持以下基门:

['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'unitary', 'initialize', 'kraus']

话虽如此,你可以在运行转换器时提供额外的参数,例如 basis_gatescoupling_map,以定义你想要使用的基门,并指定量子比特如何连接,以便在转换多量子比特门时使用。我们在这章中不会深入探讨这一点,但可以查看 Qiskit® 帮助以获取更多信息:

>>> from qiskit.compiler import transpile

>>> help(transpile)

现在看看 还有更多 部分,看看事情如何迅速变得更加复杂。

还有更多...

这个快速配方快速展示了转换器为非常简单的量子程序做了什么,并说明了转换器如何将你的通用电路转换为可以直接在量子芯片上执行的电路。

以下快速示例说明了如果要求转换器构建与后端物理布局完全不匹配的电路,可能会遇到哪些复杂性。你可以在示例代码中测试这些电路,看看你是否得到类似的结果。

运行示例代码时,测试以下输入:

添加 H 和 H + 障碍 – 多个门和障碍电路元件

单个门的转换并不会给转换器留下太多工作要做,但一旦你添加了更多的门,事情会迅速变得更加复杂。记住,量子门通常是围绕三个轴 xyz 的旋转。如果连续添加两个门,转换器会试图通过将多个门组合成单个基门来简化电路(从而使其更短;更浅)。

在示例代码中,我们在 X 门之后添加了一个 Hadamard 门来扩展量子电路:

# Add H
qc.x(0)
qc.h(0)

当你现在运行电路时,你应该能看到类似这样的东西:

![图 6.26 – 简化的转换电路图片

图 6.26 – 简化的转换电路

如您所见,编译器将两个门合并为一个,通过预览门组合的结果来简化并缩短电路,并将其编码为 U 门。然而,并不总是希望这种简化。例如,正如我们在第二章中“快速介绍量子门”配方中看到的,“使用 Python 进行量子计算和量子比特”,连续两个相同的量子门可能会有效地相互抵消。在某些情况下,量子电路最终可能由重复的门组成,如果移除这些重复的门,电路将无法按预期工作。

解决方案是barrier()电路组件,它阻止编译器通过合并门来简化。通过使用H + Barrier选项,我们在 X 门和 H 门之间添加一个障碍,如下所示:

# H + Barrier
qc.x(0)
qc.barrier(0)
qc.h(0)

当您使用H + Barrier选项时,您应该看到类似以下内容:

图 6.27 – 编译器不会越过障碍

图 6.27 – 编译器不会越过障碍

现在门是逐个编译的,电路深度得到了保留。所以,没问题,对吧?嗯,这些门相当简单。当我们稍微复杂化我们的电路时会发生什么?请看下一节。

控制 Y

控制 X 门CX)是 IBM Quantum®后端的基门,但控制 YCY)不是。为了创建可以在后端运行的 CY 门,编译器添加了一些额外的基门。

通过使用Controlled-Y选项,我们添加一个cy()门,如下所示:

# Controlled Y (CY)
qc.cy(0,1)

为了创建可以在后端运行的 CY 门,编译器添加了一些额外的基门:

图 6.28 – 编译 CY 门

图 6.28 – 编译 CY 门

这里编译器做了一个小技巧,在 CX 门中的 X 前后添加 Z 旋转。如果控制量子比特为 0,我们将在 z 轴周围来回旋转。如果控制量子比特为 1,我们得到 Z 轴的负四分之一旋转,X 轴的半转旋转,然后是 Z 轴的四分之一旋转,实际上相当于半转 Y 旋转。当然,Y 旋转可以用Y(qubit) = U3(, , , `qubit)``轻松完成,但由于 CX 的控制功能不能附加到 Y 门上,我们不得不进行这种重做。

让我们看看一些其他示例。这些将说明编译器如何将看似简单的量子电路转换为可以在实际 IBM Quantum®硬件上运行的基门电路。

不符合规范的 CX

在这个例子中,我们在两个物理上未连接的量子比特之间设置了一个 CX 门。如您所见,CX 是一个基本门,因此这不应该需要任何转换,对吧?但是,在这种情况下,我们迫使转换器尝试在两个未直接连接的量子比特之间构建您的电路。在这种情况下,转换器必须将您的 CX 门映射到几个中间量子比特上,这将增加电路的复杂性。在我们的示例代码中,我们构建了这个电路:

# Non-conforming CX
qc.cx(0,4)

使用不符合规定的 CX 输入运行示例代码应该产生类似于以下的结果:

图 6.29 – 在非连接量子比特之间转换 CX 门

图 6.29 – 在非连接量子比特之间转换 CX 门

注意这个简单的单 CX 电路突然膨胀到包含七个 CX 门。这不是最有效的编码示例。如果你查看我们正在进行转换的量子计算机的耦合图,即 IBM Quantum® Ourense,原因就一目了然。量子比特 4 不能直接与量子比特 0 通信,而必须先通过 3 和 1:

图 6.30 – IBM Quantum® 5 量子比特后端之一的物理布局

图 6.30 – IBM Quantum® 5 量子比特后端之一的物理布局

对于简单的电路,原始电路和转换电路之间没有太大差异;尝试将 CX 门从量子比特 0 连接到量子比特 1,看看你得到什么。但对于更复杂的电路,电路会迅速分化。

但等等!

对于那些像我自己一样仔细检查电路并思考如果量子比特 0 设置为 1 以触发 CX 会发生什么的挑剔者,您会注意到原始电路的结果应该是代码中的barriermeasure行,并再次运行:

# Show measurement targets
qc.barrier([0,1,2,3,4])
qc.measure([0,1,2,3,4],[0,1,2,3,4])

这应该产生以下输出:

图 6.31 – 转换器也会移动测量指令

图 6.31 – 转换器也会移动测量指令

如您从新的映射中看到的那样,量子比特 1 和 3 现在被测量为经典比特 0 和 4,正如我们所期望的。最终的测量输出将是正确的。

多门 – 结合贝尔和 CSWAP 电路

最后这个例子说明了当你试图实现一个相对简单但路径有限的目标时,会出现的复杂性。我们正在创建量子比特 0 和 4 的贝尔态,并添加一个从量子比特 3 到量子比特 1 和 2 的控制-SWAP 门,以便这些量子比特交换值:

# Multi qubit circuit
qc.h(0)
qc.h(3)
qc.cx(0,4)
qc.cswap(3,1,2)

多门输入应该给出以下结果:

图 6.32 – 一个相对简单的贝尔+CSWAP 电路的复杂转换

图 6.32 – 一个相对简单的贝尔+CSWAP 电路的复杂转换

嗯,尝试这个相对简单的电路很快就变得糟糕起来。它的深度从 2 增加到 32。电路的大小也从最初的 4 个门增加到惊人的 43。看看所有那些 CX 门!

图 6.33 – 多门电路的深度和大小

图 6.33 – 多门电路的深度和大小

再次,如果你看一下 图 6.33,你就可以看到为什么会发生这种情况。我们简单地连接量子位 0 和 4 在物理上是不可行的,因为量子芯片的布局。相反,这个连接需要通过量子位 1、2 和 3 的中间量子位连接链。这些额外的连接导致了单个门的数量激增。

现在尝试以下电路,看看这如何改变计数。你应该预期深度和大小都会显著缩小,因为你现在正在为可以直接相互通信的量子位编写代码。显然,在这个噪声中等规模量子(NISQ)时代,为你要运行的程序编写后端代码仍然很重要:

图 6.34 – 更好的符合要求的 Bell + CSWAP 电路

图 6.34 – 更好的符合要求的 Bell + CSWAP 电路

正如我们在本章中看到的,高质量的编程在量子计算中与在经典计算中一样重要,如果不是更重要。编译器会尽其所能将你的电路翻译到你计划运行的底层。

如果电路变得拥挤,并且你充分利用了所有的量子位,那么电路的重写可能会使执行时间超过 T1 和 T2 时间,即使电路本身构建得非常完美,在今天NISQ 计算机上也可能产生垃圾结果。

量子编程绝对是一门艺术!

第七章:第七章:使用 Aer 模拟量子计算机

到目前为止,我们主要在我们的本地 QASM 模拟器上运行我们的量子程序,它默认模拟了一个通用纠错量子计算机——量子计算界期望或至少希望在未来几年内成为现实的机器类型。

但这些令人惊叹的机器目前还不可用,因此在本章中,我们将探索量子模拟器,无论是在本地使用Qiskit Aer还是在云端的IBM Quantum®模拟器。我们将了解如何在这些模拟的完美后端上运行您的量子电路。

然而,您也可以通过为模拟器设置噪声配置文件来模拟今天的有噪声的中规模量子NISQ)计算机,以模拟真实的 IBM Quantum®硬件,包括门错误和噪声。因此,我们也将探讨这一点。

我们还将探讨两种其他本地模拟器类型及其用途:幺正模拟器状态向量模拟器

在本章中,我们将介绍以下食谱:

  • 理解量子模拟器的使用

  • 比较 Qiskit Aer 模拟器和 IBM 量子计算机

  • 将 IBM Quantum®后端的噪声配置文件添加到本地模拟器中

  • 通过使用幺正模拟器理解您的电路

  • 使用状态向量模拟器进行诊断

本章的目标是让您在开发和测试量子程序时能够使用模拟器,这样您就不必等待您的 IBM Quantum®后端运行测试。我们将首先探索qasm_simulator后端,然后查看幺正和状态向量模拟器。

技术要求

本章中我们讨论的量子程序可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter07

理解量子模拟器的使用

量子计算机模拟器是一种软件程序,它模拟真实量子计算机的量子力学行为。模拟器在您在云端的 IBM Quantum®后端运行之前,在本地环境中测试您的量子电路非常有用。您还可以使用基于云的模拟器来测试可能还无法在实际量子计算机上运行或运行时间太长的较大量子电路。

在这个食谱中,我们将快速浏览可用的 Qiskit®模拟器——无论是在本地的Qiskit Aer上还是在云端的IBM Quantum®上。

准备工作

确保您从第一章,“准备您的环境”中的一切都正常工作。

这个菜谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter07/ch7_r1_aer.py

如何做...

让我们看看代码:

  1. 如同往常,我们首先导入所需的类。我们导入AerIBMQ,因为我们将在本地和远程使用模拟器。如果需要,我们也会加载我们的账户并获取我们的提供商:

    from qiskit import Aer, IBMQ
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 然后,我们使用backends()方法查看可用的本地 Qiskit Aer 后端:

    backends=Aer.backends()
    print("\nAer backends:\n\n",backends)
    

    之前的代码应该给出以下结果:

    图 7.1 – 本地 Aer 后端;所有模拟器

    图 7.1 – 本地 Aer 后端;所有模拟器

  3. 我们可以将这些模拟器的配置细节存储在simulators列表中,以供进一步处理。使用backend.configuration()方法提取这些信息,并遍历可用的后端,将每个的数据添加到列表中:

    simulators=[]
    for sim in range(0,len(backends)):
        backend = Aer.get_backend(str(backends[sim]))
        simulators.append(backend.configuration())
    
  4. 为了完整性,我们将通过将信息附加到列表中为 IBM Quantum®模拟器添加配置细节:

    ibmq_simulator=provider.backends(simulator=True)
    simulators.append(provider.get_backend(str(ibmq_simulator[0])). simulator[0])).configuration())
    
  5. 显示原始模拟器配置细节。让我们遍历simulators列表,打印并查看我们模拟器的可用配置细节:

    # Display the raw simulator configuration details
    print("\nSimulator configuration details:")
    for sim in range(0,len(simulators)):
        print("\n")
        print(simulators[sim].backend_name)
        print(simulators[sim].to_dict())
    

    代码应该类似于每个模拟器的以下输出:

    图 7.2 – 模拟器配置细节的堆叠

    图 7.2 – 模拟器配置细节的堆叠

    这原始的打印输出将产生大量需要处理的信息。在下一步中,我们将整理并显示一些用于比较的常见参数。

  6. 比较模拟器。

    因此,每个模拟器都有大量的信息。为了进行比较,让我们抓取一些感兴趣的参数,并为每个模拟器列出它们。就我们的目的而言,以下参数可能很有趣:

    ibmq_qasm_simulator的详细信息,你会看到这个非本地的 IBM Quantum®模拟器没有描述:

图 7.3 – ibmq_qasm_simulator 没有描述

图 7.3 – ibmq_qasm_simulator 没有描述

在代码中,我们为了完整性,使用if/elif命令为每个模拟器的local属性添加自己的描述,如果local==False则添加自己的描述:

# Fish out criteria to compare
print("\n")
print("{0:25} {1:<10} {2:<10} {3:<10}".    format("Name","#Qubits","Max shots.","Description"))
print("{0:25} {1:<10} {2:<10} {3:<10}".    format("----","-------","--------","------------"))
description=[]
for sim in range(0,len(simulators)):
    if simulators[sim].local==True:
        description.append(simulators[sim].description)
    elif simulators[sim].local==False:
        description.append("Non-local IBM Quantum             simulator")
    print("{0:25} {1:<10} {2:<10} {3:<10}".        format(simulators[sim].backend_name, 
        simulators[sim].n_qubits,         simulators[sim].max_shots, description[sim]))

之前的示例代码将产生以下类似的结果:

图 7.4 – 选择模拟器属性列表

图 7.4 – 选择模拟器属性列表

从这个列表中,我们可以获得模拟器能做什么以及每个的具体标准的概述:

qasm_simulator:这个模拟器允许你运行你的量子程序,并返回结果,就像你在一个完美的量子计算机上运行一样,没有错误和噪声,但可以选择添加错误和噪声配置文件来模拟 NISQ 后端。这个模拟器是用 C++ 编写的,并在你的本地机器上运行。

statevector_simulator:使用这个模拟器,你可以在电路的任何位置模拟你的量子比特的状态向量。这个模拟器是用 C++ 编写的,并在你的本地机器上运行。

unitary_simulator:使用这个模拟器,你可以计算电路的单位矩阵。这个模拟器是一个本地 Python 模拟器。

pulse_simulator:这是一个基于脉冲的哈密顿模拟器,用于脉冲 Qobj 文件。使用这个模拟器,你可以通过基于脉冲的编程直接与后端量子比特交互进行测试,从而绕过标准门。

ibmq_qasm_simulator:这是该组中唯一的非本地模拟器。它的工作方式与本地 qasm_simulator 模拟器类似,但性能更高。

我们现在知道了我们可以使用哪些模拟器来工作,我们将在本章中进一步探讨它们。我们将不会触及的唯一模拟器是 pulse_simulator,因为使用这个模拟器超出了本书的范围。如果你感兴趣,请查看食谱末尾的 深入真实量子硬件核心 链接。

还有更多...

查看两个 QASM 模拟器的性能数据——量子比特数量最大射击次数。它们都允许你玩大约 30 个量子比特,并且每次运行都可以进行数千次射击。那么,它们之间有什么区别呢?

在运行模拟器时需要记住的一点是,它们正在模拟量子计算机——正是我们期望在未来能够解决复杂问题的量子计算机。这本质上意味着在像你的这样的经典计算机上模拟量子计算机,每增加一个量子比特,其复杂性大约会增加两倍。

对于在线的 ibmq_qasm_simulator 模拟器,由于它运行在 IBM POWER9™ 服务器上,这是一台相当庞大的硬件设备,因此这并不一定是一个大问题。你可以向它投递规模相当大的量子程序,高达 32 个量子比特,而不会有任何问题。

相比之下,你自己的硬件则是另一回事。本地 qasm_simulator 模拟器的性能将取决于你运行它的硬件。当你开始感觉到本地机器上的延迟和缓慢时,可能就是时候使用在线的 ibmq_qasm_simulator 模拟器了。

另请参阅

比较 Qiskit Aer 模拟器和 IBM 量子计算机

在这个食谱中,我们将创建一个长的量子电路,交换两个量子比特之间的![img/Formula_07_001.png]状态。你会发现电路在你的本地 Qiskit Aer 模拟器上提供了完美的结果,但在实际的 IBM Quantum®机器上并不完全完美。

准备工作

本食谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter07/ch7_r2_ootb.py.

如何做到这一点...

这是代码,存储在ch7_r2_ootb.py文件中:

  1. 和往常一样,首先导入所需的类和方法,并加载你的账户:

    # Import Qiskit
    from qiskit import QuantumCircuit
    from qiskit import Aer, IBMQ, execute
    # Import visualization tools
    from qiskit.tools.visualization import plot_histogram
    from qiskit.tools.monitor import job_monitor
    # Load account
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider() 
    
  2. 选择要包含的 SWAP 门数量:

    # Enter number of SWAP gates to include with your circuit # with (default 10)
    user_input = input("Enter number of SWAP gates to use:")
    try:
       n = int(user_input)
    except ValueError:
       n=10
    n_gates=n
    
  3. 现在我们构建一个包含一排选定数量的 SWAP 门的量子电路:

    # Construct quantum circuit
    circ = QuantumCircuit(2, 2)
    circ.x(0)
    while n >0:
        circ.swap(0,1)
        circ.barrier()
        n=n-1
    circ.measure([0,1], [0,1])
    print("Circuit with",n_gates,"SWAP gates.\n",circ)
    
  4. qasm_simulator上运行电路并获取结果:

    # Select the QasmSimulator from the Aer provider
    simulator = Aer.get_backend('qasm_simulator')
    # Execute and get counts
    result = execute(circ, simulator,     shots=simulator.configuration().max_shots).result()
    counts = result.get_counts(circ)
    print("Simulated SWAP counts:",counts)
    display(plot_histogram(counts, title='Simulated counts     for '+str(n_gates)+' SWAP gates.'))
    

结果会显示一个提示,要求在 IBM Quantum®后端上运行相同的电路。在这里休息一下,继续阅读。

它是如何工作的...

当你在模拟器上运行这个量子电路时,它会完美模拟,我们得到了预期的完美 100%的结果:

Simulated SWAP counts: {'01': 10000}

如果你跟随以下电路,你会看到我们首先使用一个 X 门将量子比特q0设置为![img/Formula_02_029.png],然后我们交换量子比特q0q1 10 次。我们期望最终得到的是q0在![img/Formula_07_003.png]和q1在![img/Formula_07_004.png],或者用两个量子比特的表示法,![img/Formula_07_005.png]:

图 7.5 – 一个包含 10 个 SWAP 门的量子电路

图 7.5 – 一个包含 10 个 SWAP 门的量子电路

小贴士

注意这里的屏障门。这些是为了指示 Qiskit®编译器不要跨越屏障进行编译,并且不要通过仅仅移除连续的 SWAP 门来简化电路,因为它们会相互抵消。为了快速回顾,请参考第三章中的抛量子硬币食谱,IBM Quantum Experience® – 量子拖放

当运行 10 个 SWAP 门的程序时,你应该得到以下输出:

Simulated SWAP counts: {'01': 100000}

这在数字和以下条形图中都可以看到:

图 7.6 – 在完美的量子计算机上经过 10 个 SWAP 门后的预期结果:01

图 7.6 – 在完美的量子计算机上经过 10 个 SWAP 门后的预期结果:01

这意味着程序运行得非常完美,我们将初始的![img/Formula_07_006.png]量子比特来回交换了 10 次,最终使量子比特回到它们开始的位置,在![img/Formula_07_007.png]。没有错误。

在未来的通用纠错量子计算机中,您将能够在整个计算过程中使用完全一致的纠错逻辑量子位运行像这样的长量子电路。默认情况下,Qiskit Aer 模拟器模拟一个无错误的通用量子计算机。

然而,当您在今天的NISQ硬件上运行相同的程序时,随着您的量子电路规模和执行时间的增长,错误开始累积。为了检查这一点,您现在可以按Enter键,并在 IBM Quantum®后端上运行电路。

我们现在导入最不繁忙的后端,具有五个量子位,并在其上运行相同的电路:

# Import the least busy backend
from qiskit.providers.ibmq import least_busy
backend = least_busy(provider.backends(n_qubits=5,     operational=True, simulator=False))
print("Least busy backend:",backend)
# Execute and get counts
job = execute(circ, backend, shots=backend.configuration().    max_shots)
job_monitor(job)
nisq_result=job.result()
nisq_counts=nisq_result.get_counts(circ)
print("NISQ SWAP counts:",nisq_counts)
display(plot_histogram(nisq_counts, title='Counts for     '+str(n_gates)+' SWAP gates on '+str(backend)))

之前的代码可能会产生以下结果:

Least busy backend: ibmq_vigo
Job Status: job has successfully run
NISQ SWAP counts: {'00': 1002, '10': 585, '11': 592, '01': 6013}

这在数值和随后的条形图中都可以看到:

图 7.7 – 在 10 个 SWAP 门之后,一些错误已经渗透进来;我们得到的结果不是预期的 01

图 7.7 – 在 10 个 SWAP 门之后,一些错误已经渗透进来;我们得到的结果不是预期的 01

如您所见,QASM 模拟器的清晰结果在运行于 IBM Quantum®机器之一时变得有些模糊。您很可能会仍然得到正确结果(01)的显著峰值,但也会有大量错误结果。

更多内容...

您可能会认为 10 个 SWAP 门是一个相当小的量子电路的例子,我们不应该在这么大的电路中看到这样的错误。但是,您必须记住的是,您构建的相对简单的逻辑电路将被转译成一个可以使用后端可用的基础门运行的电路。

使用以下transpile示例来打印后端的基础门,并在转译前后检查 SWAP 电路的门深度:

# Comparing the circuit with the transpiled circuit
from qiskit.compiler import transpile
trans_swap = transpile(circ, backend)
print(trans_swap)
print("Basis gates:",backend.configuration().basis_gates)
print("SWAP circuit depth:",circ.depth(),"gates")
print("Transpiled SWAP circuit depth:",    trans_swap.depth(),"gates")

之前的代码应该在 IBM Quantum®五量子位机器(如ibmq_vigo)上产生以下结果:

图 7.8 – 转译后的 10 个 SWAP 门电路的外观

图 7.8 – 转译后的 10 个 SWAP 门电路的外观

之前的代码示例应该会产生以下类似的输出:

Basis gates: ['u1', 'u2', 'u3', 'cx', 'id']
SWAP circuit depth: 12 gates
Transpiled SWAP circuit depth: 32 gates

当您在真实的量子计算机上运行时,每个门都会引入噪声和门错误。正如您在前面的图中所见,一个单独的 SWAP 门在转译后可能会变成三个连续的 CX 门,而连续添加 10 个这样的门会导致 30 个 CX 门。这可能导致一些潜在的大错误。请注意,转译门的数量取决于所选的后端,可能比本例中的 30 个更多。

参见

量子计算在 NISQ 时代及其之后,作者为加州理工学院信息与物质研究所和沃尔特·伯克理论物理研究所的约翰·普雷斯科尔,地址:美国加利福尼亚州帕萨迪纳,加州理工学院,91125:quantum-journal.org/papers/q-2018-08-06-79/.

将 IBM Quantum®后端的噪声配置文件添加到本地模拟器中

在本菜谱中,我们找到 IBM Quantum®后端的噪声数据,以构建一个噪声配置文件,然后在我们运行模拟器时将其添加到模拟器中。这将使模拟器表现得像真实的 NISQ 后端。

准备工作

本菜谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter07/ch7_r3_noise.py

如何操作...

让我们看看以下代码:

  1. 获取可用后端的列表并选择一个进行模拟。

    我们将获取一个 IBM Quantum®后端的噪声配置文件,并将其与我们的模拟器一起使用。首先,我们使用select_backend()函数列出后端并做出选择:

    def select_backend():
        # Get all available and operational backends.
        available_backends = provider.backends(filters=lambda         b: not b.configuration().simulator and         b.configuration().n_qubits > 1 and         b.status().operational)
        # Fish out criteria to compare
        print("{0:20} {1:<10} {2:<10}".format("Name",        "#Qubits","Pending jobs"))
        print("{0:20} {1:<10} {2:<10}".format("----",        "-------","------------"))        
        for n in range(0, len(available_backends)):
            backend = provider.get_backend(str(            available_backends[n]))
            print("{0:20} {1:<10}".format(backend.name(),            backend.configuration().n_qubits),            backend.status().pending_jobs)
        select_backend=input("Select a backend (        'exit' to end): ")
        if select_backend!="exit":
            backend = provider.get_backend(select_backend)
        else:
            backend=select_backend
        return(backend)
    

    上述代码可能会导致以下列表:

    ![图 7.9 – 可用的 IBM Quantum®后端列表 图片

    图 7.9 – 可用的 IBM Quantum®后端列表

    由于我们还将量子电路运行在后台上,您应该选择一个队列时间相对较短的,以避免等待结果的时间过长。

  2. 获取噪声配置文件。可以通过使用NoiseModel.from_backend(backend)方法从后端提取噪声模型:

    def build_noise_model(backend):
        # Construct the noise model from backend
        noise_model = NoiseModel.from_backend(backend)
        print(noise_model)
        return(noise_model)
    

    噪声模型将根据您选择的后端而有所不同。这里展示了一个示例模型:

    ![图 7.10 – IBM Quantum®后端的噪声模型 图片

    图 7.10 – IBM Quantum®后端的噪声模型

    我们现在可以使用噪声模型和其他参数运行模拟器,将所选后端的 NISQ 特性应用于模拟器计算,并使其表现得像一个实际的物理后端,而不是一个完美的模拟器。

  3. 构建一个 GHZ 状态电路并在四个不同的后端上运行它。

    execute_circuit()函数用于在所有后端上运行所有变体。

    在 Python 中,我们首先获取后端的基本门和耦合图:

    def execute_circuit(backend, noise_model):
        # Basis gates for the noise model
        basis_gates = noise_model.basis_gates
        # Coupling map
        coupling_map = backend.configuration().coupling_map
        print("Coupling map: ",coupling_map)
    

    我们然后构建一个 GHZ 状态量子电路,在模拟器上执行它,并获取计数:

        circ = QuantumCircuit(3, 3)
        circ.h(0)
        circ.cx(0, 1)
        circ.cx(0, 2)
        circ.measure([0,1,2], [0,1,2])
        print(circ)
        # Execute on QASM simulator and get counts
        counts = execute(circ, Aer.get_backend(        'qasm_simulator')).result().get_counts(circ)
        display(plot_histogram(counts, title='Ideal counts         for 3-qubit GHZ state on local qasm_simulator'))
    

    然后,我们使用噪声模型和耦合图在本地和 IBM Quantum® QASM 模拟器上执行有噪声的模拟,并获取计数:

        counts_noise = execute(circ, Aer.get_backend(        'qasm_simulator'), noise_model=noise_model,         coupling_map=coupling_map,         basis_gates=basis_gates).result().get_        counts(circ)
        display(plot_histogram(counts_noise, title="Counts         for 3-qubit GHZ state with noise model on local         qasm simulator"))
        # Execute noisy simulation on the ibmq_qasm_simulator     # and get counts
        counts_noise_ibmq = execute(circ, provider.get_        backend('ibmq_qasm_simulator'),         noise_model=noise_model, coupling_map=coupling_        map, basis_gates=basis_gates).result().get_        counts(circ)
        display(plot_histogram(counts_noise_ibmq,         title="Counts for 3-qubit GHZ state with noise         model on IBMQ qasm simulator"))    
    

    最后,我们在 IBM Quantum®后端上执行作业并获取计数:

        job = execute(circ, backend)
        job_monitor(job)
        counts_ibmq=job.result().get_counts()  
        title="Counts for 3-qubit GHZ state on IBMQ backend "         + backend.name()
        display(plot_histogram(counts_ibmq, title=title))
    

    最后一步是显示所有运行的收集结果:

        display(plot_histogram([counts, counts_noise,         counts_noise_ibmq, counts_ibmq], bar_labels=True,         legend=["Baseline","Noise on simulator",         "Noise on IBMQ simulator", "IBM Q backend"],         title="Comparison"))
    

在我们遍历这四个 GHZ 电路的执行过程中,我们将看到初始完美的模拟,它只提供输出,概率约为 50%,是如何被错误污染的;我们得到所有可能的状态输出。

在最终输出中,我们比较所有执行与所选后端的最终执行。运行程序时您可能会看到以下一系列截图:

  1. 首先,我们在无噪声的模拟器上运行,得到以下输出:图 7.11 – 首次在本地模拟器上理想运行

    图 7.11 – 首次在本地模拟器上理想运行

  2. 然后,我们添加噪声模型并再次运行:图 7.12 – 将噪声模型添加到本地模拟器

    图 7.12 – 将噪声模型添加到本地模拟器

    如我们现在所看到的,我们不再得到漂亮、干净、完美的量子计算机结果,而是得到一个结果,这个结果与您在实际上运行电路时得到的结果非常接近。

  3. 我们再次通过在在线 IBM Quantum® QASM 模拟器上运行带有噪声模型进行测试:图 7.13 – 将噪声模型添加到 IBM Quantum® 模拟器

    图 7.13 – 将噪声模型添加到 IBM Quantum® 模拟器

  4. 最后,我们再次运行电路,现在是在我们最初选择的后端上:

图 7.14 – 在 IBM Quantum® 后端运行电路

图 7.14 – 在 IBM Quantum® 后端运行电路

这次运行的结果应该与我们根据从实际 IBM Quantum® 后端推导出的噪声模型进行的模拟运行相似。

我们现在可以将所有结果合并到一个单独的图表中进行比较:

图 7.15 – 模拟器和 IBM Quantum® 后端之间的输出比较

图 7.15 – 模拟器和 IBM Quantum® 后端之间的输出比较

在最终输出中,您可以看到,添加了噪声模型的模拟器在统计上至少表现得像它们所模拟的 IBM Quantum® 后端。如您所见,对于 基线 Aer 模拟,我们只得到 GHZ 状态的预期 结果,但对于所有其他运行,我们还得到了 噪声,如 等结果。

参见

Qiskit Backend Specifications for OpenQASM and OpenPulse Experiments,arXiv,IBM Research 等人,2018 年 9 月 11 日:arxiv.org/pdf/1809.03452.pdf

通过使用单位矩阵模拟器理解您的电路

结果表明,任何仅由门组成的有效量子电路都可以被转换成一个单位矩阵,该矩阵描述了每个可能的状态向量输入的预期结果。正如您在 第二章 中所看到的,使用 Python 进行量子计算和量子比特,每个量子门本身就是一个 单位矩阵,构成完整量子电路的单位矩阵的组合本身也可以描述为 单位

Qiskit® 允许您使用 Qiskit Aer unitary_simulator 模拟器返回与您的量子电路相对应的单位矩阵。您运行作业的方式与 qasm_simulator 作业相同。

当运行unitary_simulator时,您只运行电路一次。然后,我们可以使用返回结果上的get_unitary(qc)方法查看电路的单位算符,例如这个使用 Hadamard 门的单量子比特叠加电路:

图 7.16 – 带有一个 Hadamard 门的量子电路

图 7.16 – 带有一个 Hadamard 门的量子电路

该电路对应以下单位算符:

[[ 0.707+0.j  0.707+0.j]
[ 0.707+0.j -0.707+0.j]]

在更清晰的打印输出中,它将如下所示:

您可能认出这是Hadamard 门矩阵,这正是它所代表的。就像这样,您可以使用单位算符模拟器返回任何有效量子电路的单位算符矩阵。这正是我们将在这个食谱中探讨的内容。

我们将创建几个简单的量子电路,并通过单位算符模拟器运行它们以获取单位算符矩阵。然后,我们将 Qiskit®单位算符与电路表示的门的组合的理论单位算符进行比较。

最后,我们在qasm模拟器上运行电路,并将结果与输入量子比特状态向量的计算进行比较,[1,0](对于单个量子比特)和[1,0,0,0](对于两个量子比特),它们代表所有量子比特从状态img/Formula_07_0041.png开始。

脚本包括一组我们定义的函数,用于控制电路的创建和其他需要完成的计算。

例如,我们使用circuits()函数创建三个基本的量子电路,并将它们存储在列表中以供以后使用。

在脚本中,我们还使用了两个我们自己创建的函数来处理单位算符信息:show_unitary()calc_unitary()

输入和函数调用由脚本末尾的main循环控制。

准备工作

该食谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter07/ch7_r4_unitary.py

如何操作...

  1. 在您的 Python 环境中运行ch7_r4_unitary.py

  2. 当您首次运行脚本时,您会看到一个输入菜单:图 7.17 – 输入菜单

    图 7.17 – 输入菜单

    输入一个数字以选择要运行的电路。脚本中预定义了13选项,而4选项允许您从 IBM Quantum Experience®输入 QASM 代码进行测试,就像我们在第三章在两个世界之间移动食谱中所做的那样,IBM Quantum Experience® – 量子拖放

    重要:无测量指令

    如果您在量子电路中包含测量指令,您必须在提交为输入之前将其删除。如果代码包含测量电路,模拟器将因 Aer 错误而崩溃。

  3. 在你选择了要探索的电路之后,程序会创建我们需要的电路,并将它们作为列表返回:

    def circuits():
        circuits=[]
        # Circuit 1 - one qubit in superposition
        circuit1 = QuantumCircuit(1,1)
        circuit1.h(0)
        # Circuit 2 - two qubits in superposition
        circuit2 = QuantumCircuit(2,2)
        circuit2.h([0,1])
        # Circuit 3 - two entangled qubits
        circuit3 = QuantumCircuit(2,2)
        circuit3.h([0])
        circuit3.cx(0,1)
        # Bundle the circuits in a list and return the list
        circuits=[circuit1,circuit2,circuit3]
        return(circuits)
    
  4. 我们现在将选定的电路发送以打印单位算符。

    show_unitary() 中,我们将后端设置为 unitary_simulator 并运行电路。从执行结果中检索返回的单位算符,并以矩阵形式打印:

    # Calculate and display the unitary matrix 
    def show_unitary(circuit):
        global unit
        backend = Aer.get_backend('unitary_simulator') 
        unit=execute(circuit, backend).result().        get_unitary(qc)
        print("Unitary matrix for the circuit:\n-------------        ------------------\n",unit)
    
  5. 最后,使用单位算符来计算电路的预测结果,并在 qasm_simulator 上运行电路以进行比较。

    calc_unitary() 函数中,我们使用返回的单位算符以及量子电路作为输入。然后我们为电路指定的量子比特数量创建一个状态向量,并使用 qasm_simulator 来比较计算结果与模拟结果:

    def calc_unitary(circuit,unitary):
        # Set number of shots
        shots=1000
        # Calculate possible number of outcomes, 2^n qubits
        binary=int(pow(2,circuit.width()/2))    
        # Set the binary key for correct binary conversion
        bin_key='0'+str(int(circuit.width()/2))+'b'        
        # Create a qubit vector based on all qubits in the     # ground state  and a results list for all     # possible outcomes.
        vector=[1]
        outcomes=[format(0, bin_key)+":"]
        for q in range (1,binary):
            vector.append(0)
            outcomes.append(format(q, bin_key)+":")
        qubits=np.array(vector)    
        # Calculate the dot product of the unitary matrix and     # the qubits set by the qubits parameter.
        a_thru_d=np.dot(unitary,qubits)    
        # Print the probabilities (counts) of the calculated     # outcome.
        calc_counts={}
        for out in range (0,len(a_thru_d)):
            calc_counts[outcomes[out]]=(int(pow(abs(            a_thru_d[out]),2)*shots))
        print("\nCalculated counts:\n------------------\        n",calc_counts)    
        # Automate creation of measurement gates from number     # of qubits 
        # Run the circuit on the backend
        if circuit.width()==2:
            circuit.measure([0],[0])
        else: 
            circuit.measure([0,1],[0,1])
        backend_count = Aer.get_backend('qasm_simulator') 
        counts=execute(circuit, backend_count,shots=shots).        result().get_counts(qc)    
        # Print the counts of the measured outcome.
        print("\nExecuted counts:\n----------------\        n",counts,"\n") 
    

    总的来说,如果我们用输入 1 运行脚本,对于 单量子比特叠加态,我们将得到一个类似于以下的结果:

    图 7.18 – 单量子比特叠加态输出

    图 7.18 – 单量子比特叠加态输出

  6. 对于单量子比特叠加态,我们将创建一个仅包含 Hadamard 门的简单量子电路。这个电路的单位算符如下:

  7. 计算结果与在 QASM 模拟器上运行电路返回的计数很好地对应。

  8. 现在,测试选项 23,看看稍微复杂一些的电路的单位算符是什么样的。当你觉得你对正在发生的事情有了很好的把握时,看看下一个菜谱,了解如何将任何电路作为 QASM 字符串导入。

使用状态向量模拟器进行诊断

在这个菜谱中,我们将探索状态向量模拟器,看看你如何使用它来对你的电路进行诊断,以了解你的量子比特是如何表现的。状态向量模拟器本身不是一个量子计算机模拟器,而是一个工具,它通过一次运行你的量子电路,并返回结果量子比特状态向量。由于这些是模拟器,你可以实际上使用它们来对你的电路进行诊断测试,而不会干扰它们并破坏量子状态。

你会注意到我们在显示量子比特作为 布洛赫球 之前已经使用了状态向量模拟器,但当时我们没有深入探讨。使用布洛赫球可视化在单量子比特或多量子比特可视化中效果很好,当每个量子比特都有一个可以投影到布洛赫球上的简单确定状态时。

有不同的输出——实际上,有几个——我们将涉及到。这些中的每一个都让你能够在测量之前,在电路的特定点上展示你的量子比特的状态。

所以,这是我们将会处理的。状态向量模拟器返回一个状态向量,类似于以下示例:

  • 对于处于叠加态的量子比特:[0.707+0.j 0.707+0.j]

  • 对于贝尔态纠缠的量子比特对:[0.707+0.j 0\. +0.j 0\. +0.j 0.707+0.j]

用标准矩阵形式写出来,这些对应于以下:

  • 处于叠加态的量子比特:

  • 一个纠缠量子比特对:

我们可以尝试使用 plot_bloch_multivector() 方法来显示这些。这种可视化让你可以观察随着电路的进行,每个量子比特如何变化:

图 7.19 – 状态

图 7.19 – 状态 的单量子比特

只要量子比特可以单独表示,这种方法就适用。对于纠缠量子比特,这种可视化方法就不再适用:

图 7.20 – 状态 的纠缠量子比特对

图 7.20 – 状态 的纠缠量子比特对

正如你所见,布洛赫球体并不是一个很好的纠缠可视化工具,因为量子比特不能单独描述,只能作为一个整体。对于这些更复杂的可视化,我们可以使用 plot_state_qsphere() 方法。Q-球体可视化是 Qiskit® 的独特功能,它将量子状态显示为 Q-球体上的一或多个矢量。

Q-球体显示一个圆(表示单量子比特状态)或一个球体(表示多量子比特状态),其中有一个或多个矢量代表状态。矢量的大小和尖端表示测量指示状态的概率;对于单量子比特 Q-球体,北极代表基态,,南极代表激发态,,颜色表示状态的相位角:

图 7.21 – 状态 的单量子比特,测量 0 的概率为 100%

图 7.21 – 状态 的单量子比特,测量 0 的概率为 100%

例如,单量子比特示例表明测量状态 的概率是 1(向上指的矢量),相位角是 0。你可以使用 Q-球体来可视化我们无法用布洛赫球体可视化的纠缠量子比特对:

图 7.22 – 状态 的纠缠量子比特对,带有测量 0 或 1 的概率各为 50%

图 7.22 – 状态 的纠缠量子比特对,测量 0 或 1 的概率各为 50%

在纠缠量子比特示例中,有两种可能的结果,概率相等:(向上指的矢量)和 (向下指的矢量),两者相位角均为 0。

注意,对于双量子比特示例,你也会看到球体的赤道。赤道存在的原因是双量子比特系统有另外两种可能的结果:。在这种情况下,结果占据了赤道上的两个相对节点: 在左端极限处, 在右端:

图 7.23 – 状态 的纠缠量子比特对,带有测量 01 和 10 的概率各为 50%

图 7.23 – 处于 状态的纠缠量子比特对,测量 01 和 10 的概率均为 50%

正如我们将看到的,如果你添加更多的量子比特,Q-球将配备额外的纬度线,就像这些一样,每条纬度线代表具有相等 汉明 值的状态,或者处于状态 的量子比特数量。例如,三量子比特 Q-球将有两个纬度线,每个纬度线有三个可能的节点。

单个量子比特叠加的三种类型:

  • 三量子比特叠加状态向量:[0.354+0.j 0.354+0.j 0.354+0.j 0.354+0.j 0.354+0.j 0.354+0.j 0.354+0.j 0.354+0.j]

  • 标准矩阵形式下的三量子比特叠加:

以下结果将在 Q-球上显示:

  • 北极

  • 第一纬度线

  • 第二纬度线

  • 南极

图 7.24 – 具有等概率(等概率(  ,或 12.5%)的所有结果

图 7.24 – 具有等概率( ,或 12.5%)所有结果节点的三量子比特 Q-球示例

好的,有了这个基础,让我们直接深入。

准备工作

该配方的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter07/ch7_r5_state_vector.py

如何操作...

对于这个配方,我们将设置一个量子电路,其中所有量子比特要么是简单的叠加,要么是所有量子比特之间的纠缠。当我们构建电路时,我们将在每个门之后进行状态向量测量,并将结果存储在列表中。然后我们将打印返回的状态向量,并将它们绘制在 Bloch 球和 Q-球上,以说明量子比特在电路执行过程中的移动情况:

  1. 设置量子比特的数量。

    在这一步中,我们设置量子比特的数量,然后通过使用 se 作为输入来选择构建叠加电路或纠缠电路:

    图 7.25 – 选择量子比特数量和电路类型

    def s_vec(circuit):
        backend = Aer.get_backend('statevector_simulator') 
        print(circuit.num_qubits, "qubit quantum         circuit:\n------------------------")
        print(circuit)
        psi=execute(circuit, backend).result().        get_statevector(circuit)
        print("State vector for the",circuit.num_qubits,         "qubit circuit:\n\n",psi)
        print("\nState vector as Bloch sphere:")
        display(plot_bloch_multivector(psi))
        print("\nState vector as Q sphere:")
        display(plot_state_qsphere(psi))
        measure(circuit)
        input("Press enter to continue...\n")
    
  2. 可以使用以下命令选择状态向量模拟器:

    backend = Aer.get_backend('statevector_simulator')
    
  3. 选择该模拟器后,当你执行一个量子电路时,模拟器会一次性运行整个电路,并返回你的量子比特计算出的状态向量。对于双量子比特电路的结果应该如下所示:图 7.26 – 双量子比特电路作为 Bloch 球和 Q-球

    def measure(circuit):
        measure_circuit=QuantumCircuit(circuit.width())
        measure_circuit+=circuit
        measure_circuit.measure_all()
        #print(measure_circuit)
        backend_count = Aer.get_backend('qasm_simulator') 
        counts=execute(measure_circuit,         backend_count,shots=10000).result().        get_counts(measure_circuit)    
        # Print the counts of the measured outcome.
        print("\nOutcome:\n",{k: v / total for total in         (sum(counts.values()),) for k, v in         counts.items()},"\n")
    

    如您所见,对于一个空的双量子比特电路,我们期望以 100% 的确定性测量到 00,这可以从状态向量中看出:

    你也可以看到两个量子比特都指向图片,Q 球矢量指向图片

  4. 接下来,按Return键向一个量子比特添加一个 Hadamard 门,并再次运行显示函数:![图 7.27 – 两个量子比特,其中一个处于叠加状态,作为布洛赫球和 Q 球 图片

    图 7.27 – 两个量子比特,其中一个处于叠加状态,作为布洛赫球和 Q 球

    现在,我们看到当第二个量子比特与布洛赫矢量指向图片状态叠加时,对状态矢量的影响。查看 Q 球,我们现在看到两个可能的结果,每个结果都有相同的概率:图片图片

  5. 最后,再次按Return键添加第二个 Hadamard 门,并再次显示:![图 7.28 – 两个量子比特在叠加状态作为布洛赫球和 Q 球 图片

    图 7.28 – 两个量子比特在叠加状态作为布洛赫球和 Q 球

    我们现在已经一步一步地走过了我们的叠加步骤。

    在最后一步,你可以看到两个量子比特的布洛赫矢量都处于图片状态,这是一个图片叠加状态。状态矢量现在如下所示:

    这在最终结果中得到了反映,其中以下状态都具有相同的 25%概率,如 Q 球所示:图片

  6. 你现在可以用两个量子比特再次运行电路,但选择e来选择纠缠,以查看量子比特如何一步一步地表现。

  7. 最后,尝试用spep作为输入运行程序,通过向最后一个量子比特添加 T 门来向你的叠加或纠缠电路添加相位角。正如你从第六章,“理解 Qiskit®门库”中回忆的那样,这将向该量子比特引入图片相位,这将反映在 Q 球输出中:

![图 7.29 – 向量子比特 1 添加相位图片

图 7.29 – 向量子比特 1 添加图片相位

再次,你可以看到以 25%的确定性预期的测量结果图片,增加了图片图片状态具有图片相位的附加变化:

![图 7.30 – 现在的结果具有相位图片

图 7.30 – 现在的结果具有图片相位

注意输出计数没有变化;给量子比特添加一个相位不会改变结果的可能性。然而,相位对于其他更复杂的量子算法可能非常有用,正如我们将在第九章,“Grover 搜索算法”中看到的。

还有更多...

在这个阶段,你可能会有一种似曾相识的感觉;你之前确实见过这个,而且你是对的。翻回到第三章中的使用电路作曲家构建量子分数配方,IBM Quantum Experience® – 量子拖放,看看我们之前讨论的检查功能。我们在该配方中讨论的是在 Qiskit®中检查你的电路的相应方法。

这是对 Qiskit®中包含的模拟器的一次快速浏览。我们简要介绍了如何在编写量子程序时使用它们的最基本示例,但只是刚刚触及了所有可用功能的一角。去探索吧,亲自试一试,看看它们如何在开发你的真实量子算法时作为工具使用。

第八章:第八章: 使用 Ignis 清理你的量子行为

我们已经探索了在理想化的 Qiskit Aer 模拟器上运行我们的量子程序,并且在实际的 IBM Quantum 机器上亲自动手。我们明白真实的量子位是有噪声的,我们无法期望量子计算机能够解决任何具有重大实际意义的现实世界问题(至少目前还不能)。通往这一未来应用的途径在于对抗和减轻噪声和错误,而 Qiskit Ignis 就在这条道路上。

Qiskit® 包含了许多自动化功能,例如根据连接性和性能优化分配的量子位;但这种自动化在一定程度上受到量子芯片物理布局的限制,这决定了量子位如何相互通信。通过研究量子位性能并指定你想要与你的量子程序一起使用的实际物理量子位,你可以优化你的电路以实现最佳纠缠和去相干,仅举几个例子。

在本章中,我们将探讨在相同后端的不同量子位集上运行你的程序可能会如何导致你得到不同的结果。我们还将使用 Qiskit Ignis 方法对我们的简单算法在模拟和现有硬件上进行读出校正。

最后,我们将探讨使用 Shor 码进行量子纠错,看看你如何可以通过使用多个物理量子位进行量子纠错来创建一个单独的逻辑量子位。

在本章中,我们将介绍以下内容:

  • 探索你的量子位以了解 T1、T2、错误和门

  • 比较芯片上的量子位

  • 估算你有多少时间可以用于门操作

  • 使用读出校正来纠正预期结果

  • 使用量子纠错来减轻意外情况

技术要求

本章中我们将讨论的量子程序可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter08.

探索你的量子位以了解 T1、T2、错误和门

让我们从一些快速概述开始,这些概述了当你将完美工作且经过模拟器验证的量子程序发送到实际的物理量子计算机时可能会出错的事情。正如我们所见,当我们离开完美的模拟量子位并开始使用在量子力学上工作的物理量子位时,我们也必须应对现实世界的另一个物理特性:噪声。

在量子计算机中,噪声在各个后端之间、后端上的量子位之间、不同类型的门之间以及每个量子位的读出之间都存在差异。构建和编程量子计算机确实是一项复杂的任务。

准备工作

用于此配方的文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter08/ch8_r1_gates_data.py

这个配方基于我们在第五章使用 Qiskit®工具游览 IBM Quantum®硬件中完成的工作,但这次我们特别关注那些暗示事物可能出错多种方式的量子比特属性。

我们将使用backend.properties() Qiskit®方法提取以下针对量子比特的属性:

  • t1(): 给定量子比特的 T1 或弛豫时间

  • t2(): 给定量子比特的 T2 或去相位时间

  • readout_error(): 测量过程中误读量子比特的风险

  • gate_length(): 门的持续时间,以秒为单位

  • gate_error(): 门的错误估计

示例代码

  1. 首先,我们导入所需的类并加载我们的账户:

    from qiskit import IBMQ
    print("Getting providers...")
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 我们在这里使用select_backend()来加载和显示可用的后端数据,然后提示选择一个:

    def select_backend():
        # Get all available and operational backends.
        print("Getting backends...")
        available_backends = provider.backends(filters=lambda         b: not b.configuration().simulator and         b.configuration().n_qubits > 0 and         b.status().operational)
        # Fish out criteria to compare
        print("{0:20} {1:<10}".format("Name","#Qubits"))
        print("{0:20} {1:<10}".format("----","-------"))        
        for n in range(0, len(available_backends)):
            backend = provider.get_backend(str(            available_backends[n]))
            print("{0:20} {1:<10}".format(        backend.name(),backend.configuration().n_qubits))
        select_backend=input("Select a backend ('exit' to         end): ")
        if select_backend!="exit":
            backend = provider.get_backend(select_backend)
        else:
            backend=select_backend
        return(backend)F
    
  3. display_information(backend)函数检索后端信息,如量子比特数量和量子比特耦合图,然后使用这些信息遍历后端量子比特以检索 T1、T2、读出错误和门信息。该函数包含两部分。

    首先,我们收集量子比特信息:

    def display_information(backend):
        basis_gates=backend.configuration().basis_gates
        n_qubits=backend.configuration().n_qubits
        if n_qubits>1:
            coupling_map=backend.configuration().coupling_map
        else:
            coupling_map=[]
        micro=10**6
    

    然后,我们打印出基本量子比特信息和每个门的具体量子比特信息:

        for qubit in range(n_qubits):
            print("\nQubit:",qubit)
            print("T1:",int(backend.properties().            t1(qubit)*micro),"\u03BCs")
            print("T2:",int(backend.properties().            t2(qubit)*micro),"\u03BCs")
            print("Readout error:",round(backend.            properties().readout_error(qubit)*100,2),"%")
            for bg in basis_gates:
                if bg!="cx":
                    if backend.properties().                   gate_length(bg,[qubit])!=0:
                        print(bg,round(                        backend.properties().gate_                        length(bg,[0])*micro,2),"\                        u03BCs", "Err:",round(backend.                        properties().gate_error(bg,                        [qubit])*100,2),"%") 
                    else:    
                        print(bg,round(                       backend.properties().gate_                       length(bg,[0])*micro,2),"\                       u03BCs", "Err:",round(backend.                       properties().gate_                       error(bg,[qubit])*100,2),"%")
            if n_qubits>0:
                for cm in coupling_map:
                    if qubit in cm:
                        print("cx",cm,round(                       backend.properties().gate_                       length("cx",cm)*micro,2),"\                       u03BCs", "Err:",round(backend.                       properties().gate_                       error("cx",cm)*100,2),"%")
    
  4. 主函数调用select_backend()display_information(backend)函数,以帮助您查看所选后端的所有量子比特信息:

    def main():
        backend=select_backend()
        display_information(backend)
    if __name__ == '__main__':
        main()
    

如何操作...

要探索特定后端的量子比特属性,请按照以下步骤操作:

  1. 在您的 Python 环境中运行ch8_r1_gates_data.py

    脚本加载 Qiskit®并获取并显示可用的后端列表,如下所示:

    ![图 8.1 – 选择一个后端进行调研 img/Figure_8.1_B14436.jpg

    图 8.1 – 选择一个后端进行调研

  2. 当提示时,输入您想要查看的 IBM Quantum®后端的名称:

    我们现在为所选后端提取backend.properties(),并从这些属性中筛选并显示以下参数:量子比特读出错误、T1 和 T2 去相位时间、门长度以及后端所有基门的错误。

    ![图 8.2 – ibmq_vigo 5 量子比特后端的数据 img/Figure_8.2_B14436.jpg

图 8.2 – ibmq_vigo 5 量子比特后端的数据

它是如何工作的...

这是一大批数据,但它只代表可以收集的特定后端数据中的一小部分。为了复习,请参阅第五章使用 Qiskit®工具游览 IBM Quantum®硬件中的使用 Qiskit®探索所选后端配方。

我们将要讨论的第一部分数据是 T1T2 时间以及 读取错误

图 8.3 – qubit 0 的数据

图 8.3 – qubit 0 的数据

这组数据首先代表了当你在后端运行量子代码时,可能得不到预期结果的一些物理原因:

  • T1,或弛豫 时间:T1 值,如图 8.3 中所示,以 形式展示,是量子位从“激发”状态 自发弛豫到基态 所需时间的统计值。本质上,T1 是对你在量子位上执行高质量操作可利用时间的估计。

  • T2,或去相位 时间:与 T1 类似,T2 值,如图 8.3 中以 ms 形式展示,是量子位相位信息丢失的度量。相位变化的一个例子是当状态 自发变为 。同样,如果你的电路运行时间开始接近 T2 时间,你的读取数据质量将受到影响。

现在我们来看一下我们收集到的其他数据:

图 8.4 – qubit 0 的数据

图 8.4 – qubit 0 的数据

readout_errorgate_lengthgate_error 代表了你可以运行在每个量子位上的门的质量。

  • readout_error:读取错误率,如图 8.4 中以百分比形式展示,是你在读取量子位时得到错误值的概率。例如,处于状态 的量子位将被读取为 ,反之亦然。这实际上与任何其他量子位操作无关,只是最终读取坍缩量子位的错误率。我们可以对每个量子位得到一个统计图像,并减轻这些读取错误。我们将在“使用读取校正修正预期”菜谱中这样做。

  • gate_length:门长度,如图 8.4 中以 形式展示,代表调整与门相对应的量子位所需的时间。如果你查看返回的数据,你会看到 U3 门的长度的量级可能是一个微秒的二十分之一左右,而 T1/T2 时间可能比这长得多。然而,这并不意味着你可以在那个时间段内添加数百或数千个这样的门并期望结果会很好。这就是门错误出现的地方。

  • gate_error:门错误,如图 8.4 中所示,以百分比形式显示,是门执行预期结果准确性的统计值。正如你所见,错误范围从 0.05%到几个百分点。对于只有几个门的短路,我们可以多次运行电路并统计推导出正确的值,即使门错误出现。对于数百或数千个门的较长电路,即使是这些小的门错误也开始产生影响。在第九章Grover 搜索算法中,你将构建包含数百个门的量子电路。

记住从第五章什么是 IBM Quantum®机器?配方中,使用 Qiskit®游览 IBM Quantum®硬件,门不是像经典计算中构成门的晶体管束这样的物理事物。相反,量子门逻辑由一系列发送到并相互作用于低温冷却的量子位的微波脉冲组成。因此,门的质量取决于很多因素:构成物理量子位的约瑟夫森结和共振电路的物理特性,载波波和门编码波包的准确性,微波共振器,液氦冷却器,等等。

还有更多

你也可以从 IBM 量子体验®获取后端量子位数据。

让我们来看一看:

  1. quantum-computing.ibm.com登录 IBM 量子体验®。

  2. 欢迎页面上,右侧你会看到一个可用的后端列表:图 8.5 – IBM 量子体验®首页

    图 8.5 – IBM 量子体验®首页

  3. 点击你感兴趣的后端,例如,ibmq_vigo,以查看芯片布局和附加信息:图 8.6 – ibmq_vigo 后端的详细信息

    图 8.6 – ibmq_vigo 后端的详细信息

  4. 点击下载校准以获取包含量子位信息的 CSV 文件。下载的校准数据如下截图所示:

图 8.7 – 从 IBM 量子体验®下载的校准数据

图 8.7 – 从 IBM 量子体验®下载的校准数据

你现在可以将数据带入你喜欢的电子表格软件进行进一步处理,如需。

比较芯片上的量子位

在之前的配方中,我们查看了一些你可以从 IBM Quantum®硬件中获取的信息片段,说明了今天 NISQ 机器的性质。在这个配方中,我们将展示一个所选 IBM 后端不同量子位的真实比较。

我们将在三个不同的设置上运行相同的贝尔态量子程序:理想量子计算机(qasm_simulator),最佳量子比特对,以及 5 量子比特、最不繁忙的 IBM Quantum®机器上最差的量子比特对。

我们将打印并绘制最终结果,以比较理想结果( 在 50%)与实际结果( 的概率混合)来展示今天的量子计算机仍有很长的路要走。

准备工作

下面的食谱中所需文件可从以下链接下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter08/ch8_r2_compare_qubits.py

示例代码

  1. 首先,我们导入所需的类和方法,并加载我们的账户。在这个食谱中,我们结合了书中早期的一些重要概念,例如模拟器和噪声模型:

    from qiskit import IBMQ, Aer, QuantumCircuit, ClassicalRegister, QuantumRegister, execute
    from qiskit.tools.monitor import job_monitor
    from qiskit.visualization import plot_histogram, plot_error_map
    from IPython.core.display import display
    print("Getting provider...")
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. select_backend() 函数允许您选择一个可用的后端。您还可以让系统选择最不繁忙的一个:

    def select_backend():
        # Get all available and operational backends.
        available_backends = provider.backends(filters=lambda         b: not b.configuration().simulator and         b.configuration().n_qubits > 1 and         b.status().operational)
        # Fish out criteria to compare
        print("{0:20} {1:<10} {2:<10}".        format("Name","#Qubits","Pending jobs"))
        print("{0:20} {1:<10} {2:<10}".format("----",        "-------","------------"))        
        for n in range(0, len(available_backends)):
            backend = provider.get_backend(str(            available_backends[n]))
            print("{0:20} {1:<10}".format(backend.            name(),backend.configuration().            n_qubits),backend.status().pending_jobs)
        select_backend=input("Select a backend ('LB' for         least busy): ")
        if select_backend not in ["LB","lb"]:
            backend = provider.get_backend(str(            select_backend))
        else:
            from qiskit.providers.ibmq import least_busy
            backend = least_busy(provider.backends(            filters=lambda b: not b.configuration().            simulator and b.configuration().n_qubits > 1             and b.status().operational))
        print("Selected backend:",backend.status().       backend_name)
        return(backend)
    
  3. 提取最佳和最差 CX 门性能信息,然后循环遍历 CX 门耦合,以找到最佳和最差性能的连接,然后将此信息作为 cx_best_worst 列表返回以供后续使用。现在我们可以查看我们存储的最佳和最差性能的 CX 门信息。为了验证我们收集到的信息是否正确,我们可以显示后端的错误图,并检查 CX 连接器是否确实代表了最佳和最差:

    def get_gate_info(backend):
        # Pull out the gates information.
        gates=backend.properties().gates
        #Cycle through the CX gate couplings to find the best     # and worst 
        cx_best_worst = [[[0,0],1],[[0,0],0]]
        for n in range (0, len(gates)):
            if gates[n].gate ==  "cx":
                print(gates[n].name, ":", gates[n].                parameters[0].name,"=",                 gates[n].parameters[0].value)
                if cx_best_worst[0][1]>gates[n].                parameters[0].value:
                    cx_best_worst[0][1]=gates[n].                    parameters[0].value
                    cx_best_worst[0][0]=gates[n].qubits
                if cx_best_worst[1][1]<gates[n].                parameters[0].value:
                    cx_best_worst[1][1]=gates[n].                    parameters[0].value
                    cx_best_worst[1][0]=gates[n].qubits
        print("Best cx gate:", cx_best_worst[0][0], ",",         round(cx_best_worst[0][1]*100,3),"%")
        print("Worst cx gate:", cx_best_worst[1][0], ",",         round(cx_best_worst[1][1]*100,3),"%")
        return(cx_best_worst)
    
  4. 创建两个针对所选后端尺寸的量子电路。收集到量子位信息后,我们可以创建一个量子程序,该程序指定最佳和最差量子位对的 CX 门。这就是我们使用之前提取的量子位变量。首先,我们构建两个电路(qc_bestqc_worst),它们具有根据所选后端正确的量子位数量。该信息是通过 backend.configuration().n_qubits 方法收集的。我们使用之前创建的 cx_best_worst 列表将 H 和 CX 门放置在正确的量子位上,然后打印电路:

    def create_circuits(backend, cx_best_worst):
        print("Building circuits...")
        q1 = QuantumRegister(backend.configuration().        n_qubits)
        c1 = ClassicalRegister(backend.configuration().        n_qubits)
        qc_best = QuantumCircuit(q1, c1)
        qc_worst = QuantumCircuit(q1, c1)
    
        #Best circuit
        qc_best.h(q1[cx_best_worst[0][0][0]])
        qc_best.cx(q1[cx_best_worst[0][0][0]], q1[cx_best_        worst[0][0][1]])
        qc_best.measure(q1[cx_best_worst[0][0][0]], c1[0])
        qc_best.measure(q1[cx_best_worst[0][0][1]], c1[1])
        print("Best CX:")
        display(qc_best.draw('mpl'))
    
        #Worst circuit
        qc_worst.h(q1[cx_best_worst[1][0][0]])
        qc_worst.cx(q1[cx_best_worst[1][0][0]], q1[cx_best_        worst[1][0][1]])
        qc_worst.measure(q1[cx_best_worst[1][0][0]], c1[0])
        qc_worst.measure(q1[cx_best_worst[1][0][1]], c1[1])
    
        print("Worst CX:")
        display(qc_worst.draw('mpl'))
    
        return(qc_best,qc_worst)
    
  5. 在后端运行最佳和最差电路。所有部件组装完毕后,我们现在可以运行最佳电路,然后是最差电路。当然,我们还想在完美的 qasm_simulator 上使用与实际后端运行时相同数量的量子位进行基准测试。在本地模拟器上创建并运行一个基准电路。打印最佳、最差和基线量子位对的成果,并在图表中绘制结果。我们还可以使用 Qiskit® 直方图功能以图表形式清晰地显示结果。

  6. 我们首先显示最佳和最差 CX 对电路,并在所选后端上运行这些电路:

    def compare_cx(backend,qc_best,qc_worst):
        print("Comparing CX pairs...")
        print("Best CX 2:")
        display(qc_best.draw('mpl'))
        job_best = execute(qc_best, backend, shots=8192)
        job_monitor(job_best)
        print("Worst CX 2:")
        display(qc_worst.draw('mpl'))
        job_worst = execute(qc_worst, backend, shots=8192)
        job_monitor(job_worst)
    
  7. 然后我们构建一个通用的 CX 电路(贝尔电路),并在本地的 qasm_simulator 上运行以获得基线结果:

        q = QuantumRegister(backend.configuration().n_qubits)
        c = ClassicalRegister(backend.configuration().        n_qubits)
        qc = QuantumCircuit(q, c)
        qc.h(q[0])
        qc.cx(q[0], q[1])
        qc.measure(q[0], c[0])
        qc.measure(q[1], c[1])
        backend_sim = Aer.get_backend('qasm_simulator')
        job_sim = execute(qc, backend_sim)
    
  8. 最后,我们收集最佳、最差和基线作业结果。然后我们打印它们,并在图表中一起显示以进行比较:

        best_result = job_best.result()
        counts_best  = best_result.get_counts(qc_best)
        print("Best qubit pair:")
        print(counts_best)
        worst_result = job_worst.result()
        counts_worst  = worst_result.get_counts(qc_worst)
        print("Worst qubit pair:")
        print(counts_worst)
        sim_result = job_sim.result()
        counts_sim  = sim_result.get_counts(qc)
        print("Simulated baseline:")
        print(counts_sim)
        display(plot_histogram([counts_best, counts_worst,                            counts_sim],                            title = "Best and worst qubit                            pair for: " + backend.name(),                            legend = ["Best qubit                            pair","Worst qubit                            pair","Simulated baseline"],
                               sort = 'desc',
                               figsize = (15,12),
                               color = ['green',                            'red','blue'],
                               bar_labels = True))
    
  9. 最后,main函数将所有这些整合在一起:

    def main():
        backend=select_backend()
        cx_best_worst=get_gate_info(backend)
        qc_best, qc_worst=create_circuits(backend,         cx_best_worst)
        compare_cx(backend,qc_best,qc_worst)
    
    if __name__ == '__main__':
        main()
    

如何完成...

IBM Quantum®后端是实际的物理半导体电路,每个后端的行为略有不同。此外,量子比特通过物理连接连接起来,以便能够以你在量子程序中指定的方式直接纠缠它们。这种类型的量子比特通信只能按照我们在第五章中查看的耦合图直接进行,该图在“使用 Qiskit®游览 IBM Quantum®硬件”菜谱中进行了可视化。

在这个菜谱中,我们从所选后端提取 2 量子比特通信的错误率。然后我们选择最佳和最差的量子比特对,并在每一对上运行相同的量子程序,以查看程序结果如何不同。

让我们看看它是如何完成的:

  1. 在你的 Python 环境中运行ch8_r3_time.py。该脚本加载 Qiskit®并获取并显示可用的后端列表:![Figure 8.8 – 首先,我们选择一个后端进行测试,例如 ibmq_santiago img/Figure_8.8_B14436.jpg

    图 8.8 – 首先,我们选择一个后端进行测试,例如 ibmq_santiago

    输入你想要测试的后端名称,或者输入LB让系统为你选择最不繁忙的系统。

  2. 最佳和最差 CX 门性能信息以列表和错误图的形式显示:![Figure 8.9 – ibmq_santiago 量子比特组合的各种 CX 门错误 img/Figure_8.9_B14436.jpg

    图 8.9 – ibmq_santiago 量子比特组合的各种 CX 门错误

    为了验证我们收集到的信息是否正确,我们显示了后端错误图。

    查看图 8.10 中的 CNOT 错误率图例,并验证我们选择的 CX 连接器确实是最佳[1,2]和最差[3,4]:

    Figure 8.10 – 显示 ibmq_santiago 最佳[1,2]和最差[3,4] CX 连接器的错误图

    图 8.10 – 显示 ibmq_santiago 最佳[1,2]和最差[3,4] CX 连接器的错误图

  3. 为所选后端创建了两个量子电路,并显示出来。这些电路代表了后端最佳和最差的 CX 连接。

    下一个图中显示了后端最佳 Bell 电路:

    ![Figure 8.11 – 最佳性能 CX 门的 Bell 态电路 img/Figure_8.11_B14436.jpg

    ...
    trans_qc_best = transpile(qc_best, backend)
    print("Transpiled qc_best circuit:")
    display(trans_qc_best.draw())
    ...
    

    前述代码的结果可能如下所示:

    ![Figure 8.13 – 最佳性能 CX 门的重构 Bell 态电路 img/Figure_8.13_B14436.jpg

    图 8.13 – 最佳性能 CX 门的重构 Bell 态电路

    如你所见,当我们遵循量子比特耦合图时,我们的重构 CX 电路与原始电路完全一样。

  4. 我们现在在后端上运行最佳和最差电路,同时在一个基准执行 Aer 模拟器上的相同电路:![Figure 8.14 – 最佳、最差和基准 CX 门对的结果 img/Figure_8.14_B14436.jpg

    图 8.14 – 最佳、最差和基准 CX 门对的结果

  5. 最后,我们将结果绘制成图表进行比较:

图 8.15 – 5 量子比特 ibmq_santiago 后端的基准、最佳和最差结果

图 8.15 – 5 量子比特 ibmq_santiago 后端的基准、最佳和最差结果

你现在有了直观的视图,以及数值证据,表明芯片上的量子比特和门在性能上有所不同。

看看这里的结果。我们预计模拟的基线(蓝色条形)只在预期的 50/50 分布下返回完美的结果 。注意模拟器没有蓝色 结果。

在实际机器上,结果会受到量子比特错误的影响,如红色(最差)和绿色(最佳)条形所示,IBM 后端对所有组合 返回有噪声的结果,最佳量子比特对略低于最差对。

更多内容...

请记住,你看到的结果不仅基于 CNOT 耦合错误,还基于量子比特错误以及你的量子比特的读写错误。要完全理解这类运行的结果,你需要考虑错误缓解。

参见

估算你可以用的时间进行门操作的数量

除了我们在前两个菜谱中探讨的门错误之外,你的菜谱的最终结果还取决于我们在运行的量子比特的另一个物理方面:T1 和 T2 时间。我们首先在 探索你的量子比特以了解 T1、T2 和错误 菜谱中讨论了这些:

  • T1,或弛豫时间:T1 值是量子比特从“激发”状态 到基态 自发弛豫所需时间的统计值。本质上,T1 是你可用于对量子比特执行高质量操作的微秒级上限。

  • T2,或去相位时间:与 T1 类似,T2 值是量子比特相位信息丢失的统计度量。相位变化的一个例子是当状态 自发变为 。再次强调,如果你的电路运行时间开始接近 T2 时间,你的读出数据质量将受到影响。

使用这些数据,我们可以粗略估计我们的程序大小可能如何影响最终结果。我们不仅要考虑单个门的错误率,还要理解 T1/T2 时间如何限制可以实际运行的门数量。在我们程序返回垃圾之前,我们能塞入多少门?让我们看看。

准备工作

下面的食谱中所需的文件可以从这里下载:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter08/ch8_r3_time.py

示例代码

  1. 首先,我们导入所需的类并加载我们的账户。在这个食谱中,我们结合了书中早期的一些重要概念,例如模拟器和噪声模型:

    from qiskit import Aer, IBMQ, QuantumCircuit, execute
    from qiskit.providers.aer.noise import NoiseModel
    from qiskit.tools.visualization import plot_histogram
    from qiskit.tools.monitor import job_monitor
    from IPython.core.display import display
    print("Getting providers...")
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. select_backend() 函数允许您选择一个可用的后端:

    def select_backend():
        # Get all available and operational backends.
        print("Getting backends...")
        available_backends = provider.backends(filters=lambda         b: not b.configuration().simulator and         b.configuration().n_qubits > 0 and         b.status().operational)
        # Fish out criteria to compare
        print("{0:20} {1:<10} {2:<10}".        format("Name","#Qubits","Pending jobs"))
        print("{0:20} {1:<10} {2:<10}".format("----",       "-------","------------"))
        for n in range(0, len(available_backends)):
            backend = provider.get_backend(str(            available_backends[n]))
            print("{0:20} {1:<10}".format(backend.            name(),backend.configuration().            n_qubits),backend.status().pending_jobs)
        select_backend=input("Select a backend:\n")
        backend = provider.get_backend(select_backend)
        return(backend)
    
  3. 当传递一个 IBM Quantum® 后端名称时,display_information(backend,n_id,ttype) 函数会提取该后端量子位 0 的 T1、T2、读出错误和 id 门长度:

    def display_information(backend,n_id,ttype):
        micro=10**6
        qubit=0
        T1=int(backend.properties().t1(qubit)*micro)
        T2=int(backend.properties().t2(qubit)*micro)
        id_len=backend.properties().        gate_length("id",[0])*micro
        if ttype=="T1":
            T=T1
        else:
            T=T2
        print("\nBackend data:")
        print("\nBackend online since:",backend.        configuration().online_date.strftime('%Y-%m-%d'))
        print("Qubit:",qubit)
        print("T1:",T1,"\u03BCs")
        print("T2:",T2,"\u03BCs")
        print("Readout error:",round(backend.properties().        readout_error(qubit)*100,2),"%")
        print("Qubit",qubit,"Id length:",round(id_len,3),        "\u03BCs") 
        print(ttype,"-id =", round(T-n_id*id_len,2),        "\u03BCs",int((100*n_id*id_len)/T),"%")
        return(T)
    
  4. build_circuit(ttype,n_id) 函数接受一个数字并构建一个包含该数量 Id 门的基电路。电路以一个 X 门开始,将量子位置于 或激发态。电路的目的是等待一段时间然后测量量子位,而 Id 门非常适合这项工作;它不执行任何量子位操作,但仍然需要一定的时间来执行。如果我们等待足够长的时间,量子位将自发地松弛到基态,或 。这将根据量子位的 T1 值需要更多或更少的门。

    根据 ttype 参数的值,我们将构建以下之一:

    T1:设置一个简单的电路,将量子位置于状态 ,然后添加多个 Id 门以使时间流逝,并最终在不同电路长度下测量结果。

    T2:同样,设置一个简单的电路,将量子位置于状态 ,然后处于叠加态 并具有相 。然后添加多个 Id 门以使时间流逝,最后应用另一个 H 门并测量。如果量子位仍然处于 状态,它现在将测量为 ,但如果它自发地改变了相位,接近 ,它将以一定的概率读取为

    def build_circuit(ttype,n_id):
        qc = QuantumCircuit(1,1)
        qc.x(0)
        if ttype in ["T2","t2"]:
            qc.h(0)
        for n in range(int(n_id)):
            qc.id(0)
            qc.barrier(0)
        if ttype in ["T2","t2"]:
            qc.h(0)
        qc.measure(0,0)
        return(qc)
    
  5. 如果我们在模拟器上运行电路,我们使用 build_noisemodel(backend) 函数为所选后端构建一个噪声模型。然后我们在 execute_circuit() 中使用该噪声模型来模拟在真实后端上运行电路:

    def build_noise_model(backend):
        print("Building noise model...")
        # Construct the noise model from backend
        noise_model = NoiseModel.from_backend(backend)
        return(noise_model)
    
  6. 使用 execute_circuit(backend, circuit,noise_model, n_id) 函数,我们通过使用在 build_noisemodel() 中创建的噪声模型,在所选后端的模拟版本上运行电路:

    def execute_circuit(backend, circuit,noise_model, n_id):
        # Basis gates for the noise model
        basis_gates = noise_model.basis_gates
        # Coupling map
        coupling_map = backend.configuration().coupling_map 
        # Execute noisy simulation on QASM simulator and get     # counts
        noisy_counts = execute(circuit,         Aer.get_backend('qasm_simulator'),         noise_model=noise_model, coupling_map=coupling_        map, basis_gates=basis_gates).result().get_        counts(circuit)
        return(noisy_counts)
    
  7. main 函数可以被分解成一系列过程,从输入和信息部分开始:

    def main():
        # Set the time type
        ttype="T1"
        # Select the backend to simulate or run on
        backend=select_backend()
        back_sim=input("Enter Q to run on the selected         backend, S to run on the simulated backend:\n")
        if back_sim in ["Q","q"]:
            sim=False
        else:
            sim=True
            noise_model=build_noise_model(backend)
        n_id=int(input("Number of id gates:\n"))
        t=display_information(backend,n_id,ttype)
        qc=build_circuit(ttype,n_id)  
        # Print sample circuit
        print("\nSample 5-Id gate",ttype,"circuit:")
        display(build_circuit(ttype,5).draw('mpl'))
    

    在处理完所有输入、噪声模型和初始电路创建后,我们现在可以在纯模拟器上运行电路,然后在所选后端上运行,无论是模拟的还是 IBM Quantum®。我们将我们的结果存储在 entry 字典中,将执行电路的长度存储在 legend 数组中,然后使用它们来展示结果:

        job = execute(qc, backend=Aer.get_backend(       'qasm_simulator'), shots=8192)
        results = job.result()
        sim_counts = results.get_counts()
        print("\nRunning:")
        print("Results for simulator:",sim_counts)
        # Run the circuit
        entry={'sim':sim_counts}
        legend=['sim']
        length=n_id
        while length!=0:
            qc=build_circuit(ttype,length)
            if sim:
                noisy_counts=execute_circuit(backend,qc,                noise_model,length)
            else:
                job = execute(qc, backend=backend,                shots=8192)
                job_monitor(job)
                results = job.result()
                noisy_counts = results.get_counts()
            print("Results for",length,"Id gates:",            noisy_counts)
            entry.update({str(length):noisy_counts})
            legend.append(str(length))
            length=int(length/4)
    
  8. 最后,我们将结果字典中的结果合并到 results_array 数组中,与 legend 数组的长度匹配,然后在组合图表中显示所有结果:

        results_array=[]
        for i in legend:
            results_array.append(entry[i])
        # Display the final results
        title="ID-circuits on "+str(backend)+" with         "+ttype+"= "+str(t)+" \u03BCs"
        if sim:
            title+=" (Simulated)"
        title+=" \nOnline since: "+str(backend.        configuration().online_date.strftime('%Y-%m-%d'))
        display(plot_histogram(results_array, legend=legend,         title=title))
    

如何操作...

要探索一个量子位如何从激发态 ![img/Formula_06_023.png] 衰减到基态 ![img/Formula_06_0221.png],请按照以下步骤操作:

  1. 在你的 Python 环境中运行 ch8_r3_time.py

    脚本加载 Qiskit® 并获取并显示可用的后端列表。输入你想要测试的后端名称,然后输入 S 以在噪声模拟的后端版本上运行。最后,输入你想要包含在你的电路中的 Id 级数门数量,例如,1024

    图 8.16 – 选择后端,是否在真实后端运行,并输入 Id 级数门数量

    图 8.16 – 选择后端,是否在真实后端运行,并输入 Id 级数门

  2. 显示所选后端第一个量子位的各种数据。我们特别关注 T1 值和 Id 级数门长度。从这些数据中,我们可以估计电路运行所需的时间以及 T1 时间将消耗的百分比。我们并不特别关注门错误;Id 级数门不进行任何量子位操作,实际上只是一个延迟门:图 8.17 – 后端数据

    图 8.17 – 后端数据

  3. 我们还显示了一个包含五个 Id 级数门的有代表性电路。你的实际电路将大得多,但具有相同的架构;一长串屏障和 Id 级数门:图 8.18 – 样例 Id 电路

    图 8.18 – 样例 Id 电路

  4. 电路现在运行,首先在 Qiskit Aer 内置的 qasm_simulator 上运行以获得干净的结果,然后是在模拟或真实后端上。它从一个具有所选 Id 级数门数量的电路开始,然后依次运行更短的电路,直到达到只有一个 Id 级数门的电路:图 8.19 – 模拟的 ibmq_valencia 后端上的原始 T1 结果

    图 8.19 – 模拟的 ibmq_valencia 后端上的原始 T1 结果

  5. 最后,它收集并显示所有运行的图表结果:图 8.20 – 在模拟的 ibmq_valencia 后端上的 T1 结果

图 8.20 – 在模拟的 ibmq_valencia 后端上的 T1 结果

好吧,那么这些结果意味着什么呢?让我们看看...

如你所见,随着门数的增加,得到结果 的概率越来越低。在 1,024 个门时,我们下降到大约 70%,这几乎接近噪声。尝试将门数加倍到 2,048,看看曲线是否接近 50%左右。所以,你需要达到什么水平才能从你的电路中获得好的结果?再次查看 图 8.20 – 这次,查看 1 个 Id 门电路的末端。得到结果 的概率在 93-95%之间徘徊,这里的不确定性部分来自读出错误,在我们的案例中约为 3.5%。这暗示了在事情开始出错之前,最大电路长度约为 64 个 Id 门。

还要记住,这个测量只考虑了 T1 弛豫时间,并且实际上只测量了在各个电路长度下量子比特的性能,只使用了 Id 门,这些门实际上对构建实际的量子电路并不真正有用。

对于实际且有用的电路,我们还需要考虑其他因素,例如门错误、架构重编译等。这意味着你不能仅仅从这次实验中推断出你认为质量上可接受的电路数量,并将其设置为后端的门限制。回顾一下 第七章 中的 Comparing the Qiskit Aer simulator with an IBM quantum computer 菜单,使用 Aer 模拟量子计算机,以了解门错误在长电路中可能产生的影响的初步示例。

更多内容

在第一次运行之后,你可以测试一些其他场景,如接下来将要讨论的。

比较后端

尝试在不同的后端上运行相同的电路,以查看结果如何不同。IBM Quantum® 正在努力工作——开发更好的量子比特和控制电路,你可以看到较新的后端通常具有更长的 T1/T2 时间,并且量子比特的性能更好。你可以通过查看 图 8.21 中的 online since 日期来估计后端有多旧。

例如,以下是 ibmqx2 后端上 1,024 个 Id 门电路的结果,该后端于 2017 年 1 月上线。将这些结果与我们刚刚获得的 ibmq_valencia 结果进行比较。该后端自 2019 年 7 月上线:

![图 8.21 – 在较旧的、T1 较短的 ibmqx2 后端上的结果图片

图 8.21 – 在较旧的、T1 较短的 ibmqx2 后端上的结果

通过比较 图 8.20 (ibmq_valencia) 中的数据与 图 8.21 (ibmqx2) 中的数据,你可以看到对于较新的 ibmq_valencia 后端,T1 时间是旧后端的两倍以上,并且在经过 1,024 个 Id 门之后得到正确结果的可能性要高得多(70% 对比 46%)。

在 IBM Quantum® 后端上运行

现在,通过再次运行示例脚本 ch8_r3_time.py 并在提示时输入 Q,来在实际后端上测试运行相同的测试。

选择合适的后端

由于我们将运行大约半打单独的任务,整个运行时间可能会根据在后台运行任务的用户数量而有所不同。在您选择要运行的后端之前,请检查后端的待处理任务数量。

在实际后端运行时,作业监视器会提供有关您在队列中的位置的信息。对于ibmq_valencia后端,这可能会给出以下结果:

图 8.22 – ibmq_valencia 后端的原始 T1 结果

图 8.22 – ibmq_valencia 后端的原始 T1 结果

图 8.22 – ibmq_valencia 后端的原始 T1 结果

并排绘制,您可以得到一个直观的比较:

图 8.23 – ibmq_valencia 后端的 T1 结果

图 8.23 – ibmq_valencia 后端的原始 T1 结果

图 8.23 – ibmq_valencia 后端的 T1 结果

看看量子比特如何随着 Id 门数量的增加从中放松,因此等待时间变长。注意实际的后端结果与模拟结果非常吻合。

测试 T2 退相

您也可以通过在示例代码中将类型参数从"T1"更改为"T2"来测试 T2 值——即您的量子比特退相的情况:

# Main 
def main():
    # Set the time type
    ttype="T2"

在这种情况下,样本电路有何不同?由于 T2 时间测量退相,我们必须首先设置我们的量子比特以实际具有相位信息。我们以 X 门开始我们的电路,将量子比特置于状态。然后我们添加一个 H 门,将量子比特带到状态,这与状态相同,只是相位移了弧度:

图 8.24 – T2 电路包括 H 门,将我们的量子比特置于状态,并带有相位的图中

图 8.24 – T2 电路包括 H 门,将我们的量子比特置于状态,并带有相位的图中

然后,我们让时间流逝,给量子比特一点时间从初始相位中退相,然后再添加另一个 H 门,将我们带回到计算基,以便我们可以测量量子比特:

图 8.25 – ibmqx2 后端的 T2 结果

图 8.25 – ibmqx2 后端的 T2 结果

图 8.25 – ibmqx2 后端的 T2 结果

通过查看图 8.25图 8.21,你现在可以对你的量子比特的 T1 和 T2 影响有一个完整的了解。尝试运行带有更多 Id 门的任务,看看行为如何变化。

参见

要详细了解 T1 和 T2 以及如何测量它们,请参阅 Robert Loredo 所著的《用 Python 和 IBM Quantum Experience 学习量子计算》,第十一章,使用 Ignis 减轻量子错误,Packt 出版社,2020 年。

使用读出校正来校正预期的

现在我们已经了解了一些关于当我们使用我们的量子比特进行量子计算时可能会出现的问题,我们能做些什么来解决这个问题吗?这里本质上有两种方法,至少对于我们现在可用的那些小型的量子后端来说。

首先,我们可以确保我们运行的量子程序在量子比特因退相干而丢失之前有完成的机会,这就是我们之前探索的 T1 和 T2 时间。这意味着我们使程序变得简短。

其次,我们可以仔细查看各种读出错误,看看我们是否可以减轻这些错误。如果你还记得在 第七章使用 Aer 模拟量子计算机,我们可以在 qasm_simulator 中引入实际的底层量子比特数据,并让它表现得像一个 NISQ 后端。我们可以反过来操作,分析后端的测量错误,并使用这些数据创建一个缓解图来对抗错误的测量。

准备工作

这个菜谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter08/ch8_r4_ignis.py.

示例代码

为了处理读出校正的创建和运行,我们在 ch8_r4_ignis.py 脚本中构建了多个函数:

  1. 首先,我们导入所需的类和方法。你可以通过运行以下代码来导入 Qiskit® 和加载账户:

    from qiskit import Aer, IBMQ, QuantumRegister, execute
    from qiskit import QuantumCircuit
    from qiskit.tools.visualization import plot_histogram
    from qiskit.tools.monitor import job_monitor
    from IPython.core.display import display
    print("Getting providers...")
    if not IBMQ.active_account():
        IBMQ.load_account()
    provider = IBMQ.get_provider()
    
  2. 使用 select_backend(),我们加载并显示可用的后端数据,然后提示选择一个:

    def select_backend():
        # Get all available and operational backends.
        available_backends = provider.backends(filters=lambda         b: not b.configuration().simulator and         b.configuration().n_qubits > 1 and         b.status().operational)
        # Fish out criteria to compare
        print("{0:20} {1:<10} {2:<10}".        format("Name","#Qubits","Pending jobs"))
        print("{0:20} {1:<10} {2:<10}".format("----",        "-------","------------"))        
        for n in range(0, len(available_backends)):
            backend = provider.get_backend(str(            available_backends[n]))
            print("{0:20} {1:<10}".format(backend.            name(),backend.configuration().            n_qubits),backend.status().pending_jobs)
        select_backend=input("Select a backend ('exit' to         end): ")
        if select_backend!="exit":
            backend = provider.get_backend(select_backend)
        else:
            backend=select_backend
        return(backend)
    
  3. 使用 create_circuit(),我们创建了一个基本的 GHZ 状态电路,对于这个电路,我们知道预期的结果 – 公式 _008_041.png:

    def create_circuit():
         #Create the circuit
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.cx(0,1)
        circuit.cx(0,2) 
        circuit.measure_all()
        print("Our circuit:")
        display(circuit.draw('mpl'))
        return(circuit)
    
  4. simulator_results(circuit) 在本地 Qiskit Aer 模拟器上运行提供的电路:

    def simulator_results(circuit):
        # Run the circuit on the local simulator
        job = execute(circuit, backend=Aer.get_backend(        'qasm_simulator'), shots=8192)
        job_monitor(job)
        results = job.result()
        sim_counts = results.get_counts()
        print("Simulator results:\n",sim_counts)
        return(sim_counts)
    
  5. noisy_results(circuit,backend) 在提供的后端上运行提供的电路:

    def noisy_results(circuit,backend):
        # Select backend and run the circuit
        job = execute(circuit, backend=backend, shots=8192)
        job_monitor(job)
        results = job.result()
        noisy_counts = results.get_counts()
        print(backend,"results:\n",noisy_counts)
        return(noisy_counts,results)
    
  6. 这个 mitigated_results(backend,circuit,results) 函数是我们构建的主要函数,用于根据后端测量错误数据在提供的结果上运行错误缓解:

    def mitigated_results(circuit,backend,results):
        # Import the required methods
        from qiskit.providers.aer.noise import NoiseModel
        from qiskit.ignis.mitigation.measurement import         (complete_meas_cal,CompleteMeasFitter)
        # Get noise model for backend
        noise_model = NoiseModel.from_backend(backend)
        # Create the measurement fitter
        qr = QuantumRegister(circuit.num_qubits)
        meas_calibs, state_labels = complete_meas_cal(qr=qr,         circlabel='mcal')
        job = execute(meas_calibs, backend=Aer.get_        backend('qasm_simulator'), shots=8192,         noise_model=noise_model)
        cal_results = job.result()
        meas_fitter = CompleteMeasFitter(cal_results,         state_labels, circlabel='mcal')
        # Plot the calibration matrix
        print("Calibration matrix")
        meas_fitter.plot_calibration()
        # Get the filter object
        meas_filter = meas_fitter.filter
        # Results with mitigation
        mitigated_results = meas_filter.apply(results)
        mitigated_counts = mitigated_results.get_counts(0)
        print("Mitigated",backend,"results:\n",        mitigated_counts)
        return(mitigated_counts)
    
  7. 最后,main() 函数有助于封装函数流程和最终数据展示:

    def main():
       backend=select_backend()
       circ=create_circuit()
       sim_counts=simulator_results(circ)
       noisy_counts,results=noisy_results(circ,backend)
       # Analyze and error correct the measurements
       mitigated_counts=mitigated_results(circ,backend,        results)
       # Show all results as a comparison
       print("Final results:")
       display(plot_histogram([sim_counts, noisy_counts,         mitigated_counts], legend=['sim','noisy',         'mitigated']))
    

如何操作…

  1. 在你的本地 Qiskit® 环境中,运行 ch8_r4_ignis.py 示例,然后选择一个可用的后端进行测试:![Figure 8.26 – 选择一个可用的后端 Figure 8.26_B14436.jpg

    Figure 8.26 – 选择一个可用的后端

  2. 我们现在将构建我们将要测试的 GHZ 状态电路。我们知道预期的结果是 公式 _008_043.png,我们可以使用这些信息来验证我们的电路在所选后端上的运行情况,以及我们纠错的能力:![Figure 8.27 – 我们将要测试的 GHZ 状态电路 Figure 8.27_B14436.jpg

    Figure 8.27 – 我们将要测试的 GHZ 状态电路

  3. 现在脚本将在本地模拟器和选定的后端上运行电路:![Figure 8.28 – 本地 qasm_simulator 和 ibmqx2 后端的结果 Figure 8.28_B14436.jpg

    Figure 8.28 – 本地 qasm_simulator 和 ibmqx2 后端的结果

  4. 现在我们已经得到了在后端运行电路的结果,我们可以从后端拉取实际的量子比特和门数据,并构建一个噪声模型。

    该模型包括后端量子比特的测量行为统计:

    ![Figure 8.29 – 包含预期结果和统计测量误差的校准矩阵 img/Figure_8.29_B14436.jpg

    图 8.29 – 包含预期结果和统计测量误差的校准矩阵

    图 8.29中,你可以看到对角线上的预期结果,以及从对角线远离的灰色阴影表示的统计测量误差。阴影越深,得到该结果的可能性越高。

    我们可以使用这些数据在本地模拟器上重新运行电路,将测量校准数据作为输入。然后,我们可以将原始结果通过测量滤波器运行,得到以下缓解结果:

    ![Figure 8.30 – ibmq_16_melbourne 的缓解结果 img/Figure_8.30_B14436.jpg

    图 8.30 – ibmq_16_melbourne 的缓解结果

  5. 最后,我们可以绘制模拟器结果、原始后端结果和缓解结果以进行比较:

![Figure 8.31 – 模拟器、后端和缓解后端的比较结果img/Figure_8.31_B14436.jpg

图 8.31 – 模拟器、后端和缓解后端的比较结果

从最终的图表中,你可以看到对于 GHZ 态电路的预期结果img/Formula_008_042.png,大约有 50/50 的机会,并不是我们在后端得到的结果。在预期结果之间有许多噪声条。通过错误缓解,我们缩小了这些条,并将结果更接近预期。

使用量子错误纠正缓解意外情况

如前一个示例中所示,了解你的测量行为是很有好处的,以便在统计上能够纠正错误的读数。但最终,一个测量只是一个测量,对一个量子比特的测量将导致 0 或 1。如果你测量的量子比特状态最终是img/Formula_06_058.png而不是预期的img/Formula_008_045.png,即使你统计上纠正了测量错误,你的量子比特也有 100%的错误。

有很多因素可能会干扰我们的量子比特,从门错误到简单的物理因素,这些因素会导致量子比特去相干和去相位(记住 T1 和 T2 时间)。在经典计算世界中,我们可以定期检查我们的比特,并应用错误纠正编码来确保它们的行为。数字错误纠正是数字通信能够工作以及你可以播放数字媒体(如 CD、DVD 和蓝光光盘)并真正听到或看到你所期望的原因之一。

执行经典错误纠正的一种方法是将你想要传输的一个比特复制成三个比特,并在最后比较这个比特与其复制品。如果它们不同,那么至少有一个比特已经出错。简单来说,你可以然后进行多数投票并翻转有问题的比特,从而恢复原始状态。

对于量子比特来说,事情并不那么简单。首先,你不能像经典比特那样复制量子比特。相反,我们必须利用叠加纠缠

我们在第二章的“比较比特和量子比特”菜谱中详细讨论了叠加,在第四章的“抛硬币的量子作弊?——介绍贝尔态”菜谱中讨论了纠缠,这些菜谱都来自《使用 Python 的量子计算与量子比特入门》。请随时回顾以巩固知识。

让我们使用这些工具进一步探索... 继续阅读!

准备工作

这个菜谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter08/ch8_r5_shor.py

示例代码

为了处理 Shor 编码算法的创建和运行,我们将在ch8_r5_shor.py脚本中构建一系列函数:

  1. 首先,我们导入所需的函数并设置后端:

    from qiskit import QuantumCircuit, execute, Aer
    from qiskit.visualization import plot_bloch_multivector, plot_state_qsphere
    # Supporting methods
    from math import pi
    from random import random
    from IPython.core.display import display
    # Set the Aer simulator backend
    backend = Aer.get_backend('qasm_simulator')
    
  2. get_psi(qc)函数是一个老朋友,我们正在重新使用它来返回电路的状态向量,以显示 Bloch 球和 Q 球:

    def get_psi(qc):
        global psi
        backend = Aer.get_backend('statevector_simulator') 
        result = execute(qc, backend).result()
        psi = result.get_statevector(qc)
        return(psi)
    
  3. 我们不是期待第一个量子比特的错误自然发生,而是使用add_error(error, circuit, ry_error, rz_error)函数来创建四种不同类型的错误——比特翻转比特翻转 + 相位翻转Theta + phi 偏移随机

    def add_error(error, circuit,ry_error, rz_error):
        circuit.barrier([x for x in range(circuit.num_        qubits)])
        if error=="1": #Bit flip error
            circuit.x(0)
        elif error=="2": #Bit flip plus phase flip error
            circuit.x(0)
            circuit.z(0)
        else: #Theta plus phi shift and Random
            circuit.ry(ry_error,0)
            circuit.rz(rz_error,0)
        circuit.barrier([x for x in range(circuit.num_        qubits)])
        return(circuit)
    
  4. not_corrected(error, ry_error, rz_error)函数创建一个简单的 1 量子比特电路,并引入我们在主过程中选择出的错误,然后以 Bloch 球和 Q 球的形式显示结果。我们还运行了 Qiskit Aer 的qasm_simulator电路,以查看我们受污染的量子比特的结果:

    def not_corrected(error, ry_error, rz_error):
        # Non-corrected code
        qco = QuantumCircuit(1,1)
        print("\nOriginal qubit, in state ")
        display(plot_bloch_multivector(get_psi(qco)))
        display(plot_state_qsphere(get_psi(qco)))
        # Add error
        add_error(error,qco, ry_error, rz_error)
        print("\nQubit with error...")
        display(plot_bloch_multivector(get_psi(qco)))
        display(plot_state_qsphere(get_psi(qco)))
        qco.measure(0,0)
        display(qco.draw('mpl'))
        job = execute(qco, backend, shots=1000)        
        counts = job.result().get_counts()
        print("\nResult of qubit error:")
        print("-----------------------")
        print(counts)
    
  5. 现在是时候添加 Peter Shor 开发的量子纠正代码了。我们正在构建与之前相同的电路,但使用了 8 个add_error()函数。这模拟了量子比特在现实世界中的扰动:

        add_error(error,qc, ry_error, rz_error)
        print("Qubit with error... LSB can be in  and in         , with various phase.")
        display(plot_state_qsphere(get_psi(qc)))
        display(qc.draw('mpl'))
    

    比特翻转纠正的结束:在引入错误后,我们现在开始再次收集我们的量子比特,从完成比特翻转纠正并调整每个相位偏移量子比特(如果需要)开始:

        qc.cx(0,1)
        qc.cx(3,4)
        qc.cx(6,7)
        qc.cx(0,2)
        qc.cx(3,5)
        qc.cx(6,8)
        qc.ccx(1,2,0)
        qc.ccx(4,5,3)
        qc.ccx(8,7,6)
    

    相位翻转纠正的结束:类似于比特翻转的结束,我们现在关闭相位翻转纠正,对第一个量子比特应用任何必要的纠正:

        qc.h(0)
        qc.h(3)
        qc.h(6)
        qc.cx(0,3)
        qc.cx(0,6)
        qc.ccx(6,3,0)
    

    测量并打印:我们现在可以测量量子比特并打印结果:

        qc.barrier([x for x in range(qc.num_qubits)])
        qc.measure(0,0)
        print("Error corrected qubit... LSB in         with phase 0.")
        display(plot_state_qsphere(get_psi(qc)))
        display(qc.draw('mpl'))
        job = execute(qc, backend, shots=1000)        
        counts = job.result().get_counts()
        print("\nResult of qubit error after         Shor code correction:")
        print("----------------------------------------------        ----")
        print(counts)
    
  6. 程序提示输入数字选择要引入的错误,然后运行not_corrected()shor_corrected()函数:

     def main():
        error="1"
        ry_error=0
        rz_error=0
        while error!="0":
            error=input("Select an error:\n1\. Bit flip\n2\.             Bit flip plus phase flip\n3\. Theta plus phi             shift\n4\. Random\n")
            if error=="3":
                ry_error=float(input("Enter theta:\n"))
                rz_error=float(input("Enter phi:\n"))
            if error=="4":
                ry_error=pi*random()
                rz_error=2*pi*random()
            not_corrected(error, ry_error, rz_error)
            input("Press enter for error correction...")
            shor_corrected(error, ry_error, rz_error)
    

我们在这里构建的代码现在可以运行,模拟任何相位的量子错误校正和量子比特的位扰动。

如何做到这一点…

让我们尝试 Shor 码:

  1. 在你的 Python 环境中运行ch8_r5_shor.py

  2. 当提示时,输入位翻转的错误类型:1

  3. 无错误的量子比特显示:![图 8.32 – 无错误的量子比特,处于状态 图片

    图 8.32 – 无错误的量子比特,处于状态 公式

  4. 然后,添加所选错误,并显示结果。量子比特现在翻转到了公式:![图 8.33 – 选择位翻转错误的量子比特,将量子比特从 图片

    图 8.33 – 选择位翻转错误的量子比特,将量子比特从

  5. 现在,按 Enter 键创建一个新的电路并显示未受干扰的量子比特及其 8 个辅助量子比特。Q-sphere 显示了此新未受干扰电路的可能结果,所有 9 个量子比特在公式:![图 8.34 – 未受干扰的量子比特与 8 个辅助量子比特 图片

    图 8.34 – 未受干扰的量子比特与 8 个辅助量子比特

  6. 我们现在开始创建 Shor 码并添加模拟错误。Q-sphere 现在显示了一些可能的结果,因为量子比特 0、3 和 6 现在是叠加态,为这些量子比特及其纠缠对应物提供了一个概率结果。注意,量子比特 0 现在可以表现为公式:![图 8.35 – 添加了位翻转错误的量子比特,将量子比特从 图片

    图 8.35 – 添加了位翻转错误的量子比特,将量子比特从

  7. 最后,我们完成 Shor 码,显示电路的预期结果,然后在 Aer 的qasm_simulator上运行它:

图 8.36 – 错误校正后的量子比特结果,回到  再次

图 8.36 – 错误校正后的量子比特结果,回到 再次

看看 Q-sphere 和结果计数。状态向量已安全地将我们的量子比特放回公式再次;注意表示第一个量子比特的最不显著位现在是公式。结果还表明我们的量子比特被安全地错误校正,有 100%的概率为 0。

它是如何工作的…

这里是对量子比特错误校正工作原理的简要说明。我们首先通过将我们的量子比特与两个其他辅助量子比特纠缠来创建所谓的伴随子。纠缠的量子比特现在作为一个整体生活,彼此之间无法区分,只有一个例外:对量子比特的错误不是纠缠的,而是每个量子比特独有的。

在使用我们的量子位进行任何操作之前,我们首先将其与其他 2 个量子位解开纠缠;现在它又是一个独立的量子位了。现在是我们使用校验位来纠正任何错误的时候了。

要做到这一点,我们设置了一个从我们的两个校验量子位到我们感兴趣的量子位的 Toffoli(CCX)门。如果校验量子位与我们的原始量子位不同,也就是说,我们的量子位已经被干扰,CCX 门会将量子位翻转回正确状态。

那就是全部了。简单,对吧?好吧,让我们更仔细地看看。

我们可以使用两种方法,针对两种不同的量子位错误:

  • 位翻转校正:纠正翻转的量子位,从公式 _008_061和相反方向

  • 相位翻转:纠正翻转的相位,从公式 _008_062和相反方向

位翻转校正

在位校正方法中,我们设置了一个 GHZ 态纠缠电路,使用第一个量子位作为 CXX 门(或在这种情况下两个 CX 门)的控制位,其中两个额外的辅助量子位仅用作错误校正校验位,并在最终测量中不被使用:

  1. 如果第一个量子位是公式 _008_063,我们现在有以下状态:

    公式 _008_064

    如果它是公式 _008_065,我们现在有公式 _008_066 – 没有什么新东西。

  2. 在设置初始量子位及其辅助位之后,我们让世界作用于第一个量子位,可能会引入位翻转错误,例如,将公式 _008_067发送到公式 _008_068

    在接下来的图中,这通过两个 CX 门之间的两个障碍物来表示。

  3. 我们 3 个量子位现在可能处于以下两种状态:

    公式 _008_069

  4. 然后我们运行第二个 GHZ 纠缠来解开第一个量子位,最终得到以下状态:

    公式 _008_070,由于第一个量子位现在是公式 _008_071,并且公式 _008_072如果第一个量子位是公式 _008_073

  5. 在这一点上,我们通过添加一个 Toffoli 门,将两个校验量子位作为控制器,第一个量子位作为受控量子位,添加了一个极其巧妙的编码。会发生什么?

    公式 _008_074变成公式 _008_075公式 _008_076变成公式 _008_074,就像魔法一样,我们的第一个量子位回到了其原始状态公式 _008_078

图 8.37 – 位翻转量子校正电路

图 8.37 – 位翻转量子校正电路

相位翻转校正

但是,量子位与经典位还有一个关键的区别:除了 0 和 1 之外,量子位还可以有一个相位值,因此也可以有相位错误。我们能否为这个错误进行纠错?事实证明,我们可以,基本上使用相同的方法,再加上另一个巧妙的转折:

  1. 就像之前一样,我们开始时有 3 个量子位和我们的 GHZ 态:

    公式 _008_079

  2. 下一步是将我们的测量基态转换为一个可以处理相位信息的状态,通过在每个量子比特上添加一个 Hadamard 门。我们现在有以下两种状态:

    Formula 008_080.png

  3. 再次,我们让自然作用于第一个量子比特,可能最终得到一个相位偏移的第一个量子比特,如下所示:

    Formula_008_081.png

    在接下来的图中,错误发生在 H 门之间的两个屏障之间。

  4. 就像在位翻转示例中一样,我们现在再次应用 Hadamard 门和创建 GHZ 的 CXX 门,现在得到以下结果:

    Formula_008_082.png

    Formula_008_083.png

  5. 最后,Toffoli(CCX)门将量子比特三重组转换为以下:

    Formula 008_084.png

    再次,我们的第一个量子比特回到了其原始状态img/Formula_008_085.png和![img/Formula_008_086.png]:

Figure 8.38 – 相位翻转量子校正电路

Figure 8.38 – B14436.jpg

Figure 8.38 – 相位翻转量子校正电路

Shor 码

那都是很好的;我们可以处理位翻转错误以及相位翻转错误。但如果两种类型的错误都发生怎么办?毕竟,量子比特是物理实体,谁又能真正知道它们会如何表现呢?结果证明,我们也可以做到这一点。以 Shor 算法闻名(见第十章,*通过 Aqua 了解算法)的 Peter Shor 发明了 Shor 码,这是一种使用总共 9 个量子比特的相位翻转和位翻转方法的组合。第一个量子比特是我们想要进行量子纠错的目标量子比特,接下来的八个量子比特是辅助量子比特,仅用于执行纠错魔法:

Figure 8.39 – Shor 码电路

Figure 8.39 – B14436.jpg

Figure 8.39 – Shor 码电路

这里有一个简要的描述,但请务必查看Figure 8.39

  1. 使用量子比特 0、3 和 6 设置相位翻转电路的前半部分。

  2. 为量子比特 0、3 和 6 设置三个位翻转电路的前半部分。

  3. 为自然和错误发生给量子比特 0 留出一些空间。在下面三重 CX 门集之间的两个屏障之间。

  4. 为量子比特 0、3 和 6 设置三个位翻转电路的后半部分,有效地纠正这些 3 个量子比特上的任何位翻转。

  5. 为量子比特 0、3 和 6 设置相位翻转电路的后半部分,纠正这三个量子比特的任何相位偏移。

  6. 测量量子比特 0。

现在数学变得有些复杂,9 个量子比特的 Ket 表示看起来像这样,例如:|011000101img/Formula_008_090.png。更不用说 9 量子比特电路的单位矩阵看起来像什么了。

更多内容

我们运行的第一个示例,一个简单的位翻转错误,并没有真正利用 Shor 码的全部功能。尝试其他选项来模拟可能发生的任何类型的错误,从简单到非常复杂。

以下选项可用:

  • Bit flip: 这种错误将比特翻转过来,从

  • Bit flip plus phase flip: 比特翻转和相位翻转的组合。

  • Theta plus phi shift: 通过输入 theta 和 phi 角度来创建自己的错误,使你的状态向量指向 Bloch 球上的任意一点。如果你需要提醒这两个角度代表什么,请快速查看 第二章 中的 Visualizing a qubit in Python 菜谱,Python 量子计算与量子比特

  • Random: 随机错误。

参见

  • Quantum Error Correction for Beginners,Simon J. Devitt, William J. Munro, 和 Kae Nemoto,2013 年 6 月 24 日,arxiv.org/pdf/0905.2794.pdf

  • Quantum Computation and Quantum Information,作者 Isaac L. Chuang 和 Michael A. Nielsen,剑桥大学出版社,2010 年,第 10.2 节 Shor 代码

第九章:第九章: Grover 搜索算法

在本章中,我们将探讨一个相当知名的量子算法:Grover 搜索算法。我们将通过构建以下变体的电路来学习如何编码它:2 量子位版本、3 量子位版本以及 4 量子位及以上版本,以了解电路的复杂性如何随着量子位的数量增加而增长。

我们将在本地模拟器和 IBM Quantum®后端上运行我们的算法,并看到算法在相对较短的 2 量子位 Grover 电路上运行得相当好,但在需要更多量子位的更大电路上则不太理想。你的电路中的门数量会依次增加,我们在第八章中探讨的各种错误开始占主导地位。

在本章中,我们将介绍以下内容:

  • 探索量子相位回弹

  • 关于经典搜索的简要介绍

  • 构建 Grover 搜索算法

  • 使用 3 量子位 Grover 进行搜索

  • 向 Grover 搜索添加更多量子位

  • 在您的代码中使用 Grover 电路

技术要求

本章中我们将讨论的量子程序可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter09

就像我们在第六章中做的那样,理解 Qiskit®门库,我们将创建一个主要的 Python 文件来包含我们将使用的更复杂函数:ch9_grover_functions.py

除了其他函数外,此程序还包括一组核心函数,用于构建 Grover 算法:

  • create_oracle(): 此函数构建一个 2-5 量子位的 Oracle,用于正确解决方案。

  • create_amplifier(): 此函数构建 Grover 电路的相位放大部分。

  • create_grover(): 此函数将各个部分组合在一起,并返回一个可以在模拟器或真实量子计算机上运行的 Grover 电路。

我们将在构建 Grover 搜索算法的配方中进一步讨论这些内容。简而言之,这些主要函数是构建 Grover 算法所需的所有内容,而程序中的其余部分都是为了帮助可视化这个过程。

包含在ch9_grover_functions.py文件中的其他函数如下:

  • print_psi(): 此函数打印出电路的格式化良好的状态向量。

  • get_psi(): 此函数返回电路的状态向量,并将其显示为 Q 球体、Bloch 球体或普通向量。

  • print_unitary(): 这个函数会打印出电路的单位矩阵。对于这一章,我们预期单位矩阵没有虚部;我们简化了一下,只打印出实值,并使用 BasicAer 的unitary_simulator来创建单位矩阵。

  • display_circuit(): 这个函数显示电路,以及可选的电路状态向量和电路单位矩阵的 Q 球视图。

最后,我们有一组函数,我们使用这些函数在模拟器、量子计算机和转换器上运行我们的电路:

  • run_grover(): 为了完整性,包含一个函数,该函数可以在模拟器或 IBM Quantum®后端上运行你的 Grover 电路。

  • mitigated_results(): 回顾上一章,第八章用 Ignis 清理你的量子行为,我们使用这个函数在我们的 2 量子位 Grover 上运行错误缓解。正如我们将看到的,在 3 和 4+量子位电路上进行错误缓解不会产生更好的结果。

  • transpile_circuit(): 为了提供洞察,我们再次引入了在第六章理解 Qiskit®门库中的你的量子电路真正看起来是什么样子菜谱中使用的转换功能。

但在我们深入算法之前,我们将先看看许多量子算法(包括 Grover)的构建块之一——所谓的相位回弹

探索量子相位回弹

在这个第一个菜谱中,我们将更深入地研究许多量子算法的基本组成部分——量子相位回弹,它被用来让一个或多个量子位拾取第二个量子位的相位角,而不会改变第二个量子位。在构建 Grover 算法菜谱中,我们将使用相位回弹来识别搜索的正确解决方案,并放大测量该解决方案的概率。

这个菜谱需要一点数学知识来解释一些相当不直观的过程和结果,但我们会一步步讲解。这对于量子算法令人惊叹的方面是一个非常好的起点。

准备工作

这个菜谱的示例代码可以在以下位置找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter09/ch9_r1_kickback.py

这个菜谱本身相当简单,由一系列步骤组成,这些步骤将引导你通过相位回弹过程,首先是一个量子位,然后是两个量子位。

在每个步骤中,我们将使用ch9_grover_functions.py中的display_circuit()函数来显示量子位发生了什么,所以让我们先看看这个函数:

def display_circuit(circuit,psi,unitary):
    disp=True
    if disp:
        display(circuit.draw(output="mpl"))
        if psi:
            get_psi(circuit,"Q")
        if unitary:
            print_unitary(circuit)

display_circuit()函数是集合中的主要可视化函数,它接受一个量子circuit和两个逻辑参数作为输入。如果psi=True,我们将调用get_psi()函数,该函数将电路显示为 Q 球体,并调用print_psi()函数以打印结果的电路状态向量的美观版本。如果unitary=True,则调用print_unitary()函数以显示电路的单位矩阵。

在这个菜谱中,我们设置unitary=False并专注于状态向量可视化。

如何做到这一点...

让我们探索如何给量子比特添加相位:

  1. 我们首先导入所需的类、方法和display_circuit()函数,这将使我们能够显示我们所做的工作:

    from qiskit import QuantumCircuit
    from ch9_grover_functions import display_circuit
    
  2. 现在,让我们创建一个初始化为状态公式的单量子比特:

    qc1 = QuantumCircuit(1)
    display_circuit(qc1,True,False)
    

    display_circuit()函数显示了我们的量子比特初始化为状态公式的 Q 球体可视化:

    ![图 9.1 – 单量子比特设置为 图 9.1 – 单量子比特设置为

    图 9.1 – 单量子比特设置为公式

    让我们看看背后的数学,正如我们在第二章,“使用 Python 进行量子计算和量子比特”中讨论的那样。我们在这里所做的是创建一个可以像这样显示的状态向量:

    公式

    或者用角度公式公式的形式描述:

    公式

    对于我们的量子比特,初始化为公式,状态向量解析为以下内容:

    公式

  3. 设置量子比特处于叠加态:

    qc1.h(0)
    display_circuit(qc1,True,False)
    

    当量子比特处于叠加态时,Q 球体显示有相等的机会得到公式公式

    ![图 9.2 – 单量子比特在叠加态 图 9.2 – 单量子比特在叠加态

    图 9.2 – 单量子比特在叠加态

    对于处于叠加态的量子比特,具有公式,这是我们在这里要处理的,上一步的公式转化为以下形式:

    公式

    这意味着我们通过角度公式然后公式来描述量子比特的相对相位,量子比特的相位是公式公式时的相反相位,在这种情况下,公式 = 1。从方程中可以看出,相位角只影响量子比特的公式部分。这在下一步将非常重要。

  4. 现在,让我们使用 Z 门给第二个量子比特添加一个相位。

    从现在开始,我将不再展示display_circuit(qc1,True,False)代码;只需假设在每个步骤之后都包含它以显示进度:

    qc1.z(0)
    

    记住,Q 球体代表量子比特的最终状态,向量尖端的大小代表测量相应结果相对概率的大小,颜色代表结果的相对相位。

    当你测量量子比特时,相位没有影响,只有概率。在这里,你可以看到具有相角 0 的状态 有 50% 的概率给出结果 0,而具有相角 的状态 也有 50% 的机会给出结果 1:

    ![Figure 9.3 – 具有相角 的单个量子比特叠加

    ](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure__9.3_B14436.jpg)

    Figure 9.3 – 具有相角 的单个量子比特叠加

    在这个方法中,我们将使用 Z 门,也称为 相位门,将相角 添加到 状态:

    使用前面的状态向量命名法,这种转换看起来是这样的:

    具有相角 0(或者具有相角 2)的叠加态量子比特:

    具有相角 (通过 Z 门后)的叠加态量子比特:

    注意看在 前面的 + 号如何变成 - 号,这标志着状态的翻转。

  5. 现在,让我们对 2 个量子比特进行相同的步骤,给每个量子比特添加相角

    qc = QuantumCircuit(2)
    qc.h([0,1])
    qc.z(1)
    qc.z(0)
    

    跳过中间步骤,前面代码的最终结果,当使用两个 psi 函数打印时,如下所示:

    Figure 9.4 – 两个处于叠加态的量子比特,两者都具有相角

    Figure 9.4 – 两个处于叠加态的量子比特,两者都具有相角

    对于两个处于叠加态的量子比特,对于前面单个量子比特的例子,感觉上有点直观,但对于两个量子比特来说,可能会有些混乱,所以让我们一步一步来做。

    就像在 1 量子比特的例子中一样,每个量子比特可以这样描述:

    将量子比特设置在叠加态,可以写成这样:

    在我们简化的叠加态视图中,这转化为以下表达式:

    对于两个量子比特,相移变换按照以下步骤进行:

    首先,两个具有 0(或者确切地说,)相角的叠加态量子比特 :

    然后,两个处于叠加态的量子比特,第二个 () 具有相角

    最后,两个具有相角 的叠加态量子比特:

    这个最后的例子代表了前面示例代码的最终结果。Q 球有四个等可能的结果,其中两个被 标记 为具有相角 ,即

    如果你记得只有状态包含相位参数,并且如果两个量子比特都具有相位,组合的指数和为,这将导致没有相位。记住这个结果。它将在下一步出现。

  6. 将一个量子比特与一个相位角纠缠。

    到目前为止,这是数学,正如你在第一个菜谱中看到的那样,这是 Qiskit®为你构建的内容。你可以改变一个量子比特的相位,而不影响另一个。真正有趣的部分来自于当你使用 CX 门纠缠量子比特时,从量子比特 0 到量子比特 1:

    图 9.5 – 从 0 到 1 的 CX(受控非)门

    图 9.5 – 从 0 到 1 的 CX(受控非)门

    该门的单位矩阵版本如下:

    对于两个处于叠加态的量子比特,结果并不那么令人兴奋;你最终得到的是你开始时的结果:

    但现在给受控量子比特 1,即,添加一个相移,然后从前面进行计算:

  7. 这就是在示例代码中是如何实现的:

    qc = QuantumCircuit(2)
    qc.h([0,1])
    qc.z(1)
    qc.cx(0,1)
    

    同样,我们跳过了中间步骤,专注于最终结果。逐步进行,并与前面的计算进行比较:

图 9.6 – 使用相位回弹的两个纠缠量子比特,具有相同的相位

图 9.6 – 使用相位回弹的两个具有相同相位的纠缠量子比特

看看这个结果。它相当令人惊讶!

让我来解释:我们最初有两个处于叠加态的量子比特,其中一个具有相位角。然后我们使用没有相位角的量子比特作为控制量子比特,具有相位角的量子比特作为受控量子比特来纠缠量子比特。最终我们得到以下结果:

两个处于叠加态的量子比特,都具有相位角,就像我们之前手动构建的例子一样。

这被称为相位回弹,是量子算法中常用的一个技巧。在本章的剩余菜谱中,我们将查看 Grover 算法,该算法使用相位回弹将问题的正确解与相位标记出来,以区别于错误解。

对于 2 量子比特的 Grover 算法,我们将使用 CX 门(受控非)来实现,对于 3 量子比特的 Grover 算法,使用 CCX(受控受控非),最后对于 4 量子比特,使用 CCCX 门(受控受控受控非)。

简短地谈谈经典搜索

在我们运行 Grover 算法之前,让我们快速看一下一个标准的经典线性搜索算法。对于一个搜索无序数据库的经典算法,你平均需要查找给定条目的次数是 的量级,其中 N 是数据库中的项数。例如,如果你的无序数据库有四个项,你通常需要平均查找两次才能找到你的项。

准备工作

该食谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter09/ch9_r2_classic_search.py.

如何做到这一点...

让我们在一个包含四个项的小型数据库中搜索特定的两位条目:

  1. 首先,我们需要输入我们正在搜索的两个位字符串,然后尝试搜索的次数以获取一些统计数据:

    Searching in a scrambled database with 4 entries:
     ('00', '01', '10', '11')
    Enter a two bit string for the two qubit state to search for, such as '10' ('Exit' to stop):
    10
    Number of searches to test:
    20
    

    这里的两位格式将在本章的 Grover 食谱中重复使用,并将表示两个量子比特的状态向量,例如,。我们还将使用 3 位和 4 位条目来表示相应数量的量子比特。

  2. 现在脚本打乱初始数据库并运行搜索函数:

    for m in range(searches):
        database=random.sample(values,len(values))
        result=simple_search(database, oracle)
        average.append(result+1)
        search.append(m+1)
    

    simple_search() 函数接受一个数据库列表作为输入,然后遍历它直到找到我们正在搜索的条目。返回项的位置,并将其作为 search 变量中的搜索次数:

    def simple_search(database, oracle):
        for position, post in enumerate(database):
            if post == oracle:
                return position
    
  3. 最后,使用 plot_results() 函数显示收集到的统计数据:

    def plot_results(average,search,values):
        import matplotlib.pyplot as plt
        from statistics import mean 
        print("Average searches to find:", mean(average))
        # Plot the search data
        plt.bar(search, average, align='center', alpha=0.5)
        plt.ylabel('Searches')
        plt.title(str(mean(average))+' average searches\nto         find one item among '+str(len(values)))
        plt.axhline(mean(average))
        plt.show()
    

    上述代码应该创建出类似以下内容:

图 9.7 – 在四个未排序项中典型经典线性搜索的结果

图 9.7 – 在四个未排序项中典型经典线性搜索的结果

这个小的搜索示例说明了可以用来在数据库中查找单个项的一个经典算法。在这个例子中,我们统计表明,在四个项的无序数据库中查找单个条目需要大约 两次搜索 来实现,这与 N/2 搜索 的预测相匹配。

如果你向数据库中添加随机项,那么你可以确信这一点也适用于包含 8 和 16 个项的数据库,这对应于 3 位和 4 位搜索字符串。

Grover 搜索算法 食谱中,我们将看到如何使用 Grover 量子算法以 搜索的顺序找到项。这是一个平方加速。在四项数据库中差别不大,但如果你的数据库包含数十万甚至数百万项,这就有区别了。

构建 Grover 搜索算法

让我们尝一尝 Grover 的搜索算法,这是解决实际问题的更直接的量子算法之一,即在索引但未排序的数据库中查找信息。正如我们在关于经典搜索的快速插曲中讨论的那样,Grover 预计将比其经典对应物快平方倍。

在 Qiskit Terra 中,我们可以创建一个使用相位回弹神谕结合另一个巧妙技巧的实现 Grover:相位放大。相位放大增加了正确相位偏移答案的幅度,从而在测量你的电路时增加了该结果出现的概率。

首先,我们创建一个所谓的神谕函数,它被设计为接受一组初始叠加的量子比特作为输入,并通过切换正确答案的相位,同时保持错误答案的相位不变。神谕电路被称为黑盒,它被编码为从一组输入中识别答案。

在我们的例子中,我们明确编码了神谕以识别特定的答案,这感觉像是作弊。如果我们已经知道答案,那么运行 Grover 算法来找到它有什么意义呢?在我们的简单例子中,这是正确的,但神谕黑盒可能是在混合经典/量子计算程序中的任何类型的函数。

你可以这样理解:如果神谕在输入中识别出正确的答案,那么它可以识别正确的答案;它不能计算出正确的答案。

神谕单位矩阵本质上是一个单位矩阵,只有一个负数项表示解,并将对应状态的相位切换。这个单位矩阵可以使用量子电路中的标准门来实现。

在这个菜谱中,我们将构建两个量子比特的 Grover 电路。对于每一步,我们将使用 Q-sphere 和电路的状态向量来显示我们所做的工作。对于两个关键组件——神谕和放大器——我们也显示了它们所代表的单位矩阵。

准备工作

这个菜谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter09/ch9_r3_grover_main.py

脚本反过来使用三个步骤来创建 Grover 电路。让我们逐一介绍它们,并在下一节中详细介绍两个量子比特 Grover 电路的初始示例。我们在这里实现电路的基本特征与你添加更多量子比特时相同。

构建或然电路、放大电路和最终 Grover 电路的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter09/ch9_grover_functions.py.

创建或然电路

我们首先需要的是或然电路。构建或然电路以标记正确答案并引入相移的一个简单方法就是使用类似于我们在探索量子相移回波配方中构建的相移回波电路。

例如,让我们设置一个相移回波或然电路,该电路将特定结果(如)的相移回传。这意味着可能的态向量相对于所有其他可能的态向量(在这种情况下,)将相移

这就是从 Qiskit®打印出来的或然电路的样子:

Figure 9.8 – 一个相移回波或然电路及其对应正确结果的单位矩阵

Figure 9.8 – 一个相移回波或然电路及其对应正确结果的单位矩阵

这是如何工作的?让我们一步步了解电路的每个部分是如何工作的:

  1. 在第一个控制量子位上,我们添加一个 X 门以确保该量子位上的翻转成,从而触发 CX 门。

  2. 在第二个量子位上,我们首先添加一个 H 门。

    这个电路是做什么的?在这种情况下,我们想要给第二个量子位添加一个相移,这样我们就可以使用 CX 门将相移回传到第一个量子位。这里,如果第二个量子位是一个错误的,应用 H 门会给我们一个相位为 0 的叠加态,没有相移回传,解决方案没有被标记。然而,如果第二个量子位是一个正确的,H 门会给我们,它有一个相位为,然后被 CX 门迅速标记到解决方案上。

  3. 最后,在 CX 门之后,我们再添加一个 X 门将第一个量子位翻转回其初始状态,并为第二个量子位添加另一个 H 门以执行相同的操作。

我们所实现的是为或然答案的态向量标记一个相移为。只有那个组合会得到相移;没有其他组合会。

可能通过显示的单位矩阵更容易理解,其中除了正确的解决方案外,所有解决方案的对角线上都表示为 1,而正确的解决方案()表示为-1,这导致结果状态向量的相移。

这就是我们在 Python 示例中是如何做的:

def create_oracle(oracle_type,size):
    from qiskit import QuantumCircuit, ClassicalRegister,        QuantumRegister
    global qr, cr
    qr = QuantumRegister(size)
    cr = ClassicalRegister(size)
    oracleCircuit=QuantumCircuit(qr,cr)
    oracle_type_rev=oracle_type[::-1]
    for n in range(size-1,-1,-1):
        if oracle_type_rev[n] =="0":
            oracleCircuit.x(qr[n])
    oracleCircuit.h(qr[size-1])
    if size==2: 
        oracleCircuit.cx(qr[size-2],qr[size-1]);
    if size==3:
        oracleCircuit.ccx(qr[size-3],qr[size-2],qr[size-1])
    if size==4:
        oracleCircuit.mcx([qr[size-4],qr[size-3],
            qr[size-2]],qr[size-1])
    if size>=5:
        oracleCircuit.mcx([qr[size-5],qr[size-4],
            qr[size-3],qr[size-2]],qr[size-1])
    oracleCircuit.h(qr[size-1])
    for n in range(size-1,-1,-1):
        if oracle_type_rev[n] =="0":
            oracleCircuit.x(qr[n])
    return(oracleCircuit)

让我们一步步来看:

  1. create_oracle()函数的输入是一个预言机类型和大小,其中类型是一个字符串,指定了我们正在寻找的量子比特组合,例如,对于![公式 _09_068.png]组合,为 10。

  2. 接下来,我们以相反的顺序遍历预言机类型,对于字符串中的每个 0 添加一个 X 门,将其翻转成 1,正如前面的讨论所述。

    注意,对于两位字符串的预言机输入,它与 Qiskit®标记量子比特的方式相反,因此我们需要在处理之前将其反转到oracle_input_rev

  3. 然后我们在最后一个量子比特上添加一个 H 门。

  4. 现在是真正的重点。根据我们构建电路的大小,我们添加一个叠加门来处理相位回弹:

    对于两个量子比特,程序会添加一个 CX 门。

    对于三个量子比特,添加了一个 CCX 门;我们将在使用 3 量子比特 Grover 搜索配方中了解更多关于这一点。

    对于 4 和 5 个量子比特,添加了一个 MCX(多控制非)门;我们将在添加更多量子比特到 Grover 搜索配方中了解更多关于这一点。

  5. 最后,我们以相反的顺序执行 H 门和 X 门步骤,以平衡预言机电路。

我们现在有一个预言机电路,它将标记我们传递给它的预言机类型。

创建放大器电路

不论我们为预言机使用多少量子比特,放大器电路的构建方式都是相同的。它通过将相位改变的概率反射到所有解决方案的平均概率上来放大正确解决方案的概率。

这就是 2 量子比特电路的电路图:

![图 _9.9 – 一个两量子比特放大器电路及其对应的单位矩阵图 _9.9_B14436.jpg

图 9.9 – 一个两量子比特放大器电路及其对应的单位矩阵

再次,如果你看看放大器单位矩阵,可能更容易理解。如果你对相位移位的解决方案进行矩阵乘法,你会看到相位移位解决方案的概率被放大,而没有相位移位的解决方案的概率则没有。结果是,在两个量子比特 Grover 电路的特殊情况下,得到正确解决方案的概率实际上是 100%;你将在四个可能的解决方案中找到正确的解决方案,只需进行一次搜索。这真是太令人惊讶了!

在接下来的矩阵乘法中,我们将放大器矩阵与相位标记的叠加向量相乘,以得到一个只有一个可能结果的解决方案向量,![公式 _09_068.png],概率为 100%:

公式 _09_073.jpg

在我们的 Python 示例中,它是这样做的:

def create_amplifier(size):
    from qiskit import QuantumCircuit
    # Let's create the amplifier circuit for two qubits.
    amplifierCircuit=QuantumCircuit(qr,cr)
    amplifierCircuit.barrier(qr)
    amplifierCircuit.h(qr)
    amplifierCircuit.x(qr)
    amplifierCircuit.h(qr[size-1])
    if size==2: 
        amplifierCircuit.cx(qr[size-2],qr[size-1]);
    if size==3:
        amplifierCircuit.ccx(qr[size-3],qr[size-2],qr[size-1])
    if size==4:
        amplifierCircuit.mcx([qr[size-4],qr[size-3],
            qr[size-2]],qr[size-1])
    if size>=5:
        amplifierCircuit.mcx([qr[size-5],qr[size-4],
            qr[size-3],qr[size-2]],qr[size-1])
    amplifierCircuit.h(qr[size-1])
    amplifierCircuit.barrier(qr)
    amplifierCircuit.x(qr)
    amplifierCircuit.h(qr)
    return(amplifierCircuit)

create_amplifier()函数只接受大小作为输入。放大器无论预言机是什么都工作相同。正如你所看到的,它与预言机电路有些相似;它也构建了一个平衡电路:

  1. 它从所有量子比特上的 H 门开始。

  2. 第二步是在所有量子比特上添加 X 门。

  3. 再次强调,根据电路的大小,它会在中间添加一个 CX、CCX 或 MCX 门来实现相位回跳。

  4. 就像对于 Oracle 电路一样,我们现在反转初始的 X 和 H 门,以平衡电路。

我们现在有了 Oracle 和放大器;我们唯一需要做的就是将它们放在一起。

创建 Grover 电路

Grover 电路将各个部分组合在一起,在开始时添加 H 门以设置叠加态,并在结束时添加测量门以允许你运行电路。

create_grover()函数接受 Oracle 电路和放大器电路作为输入。它还接受一个布尔showsteps参数。将此参数设置为showstepsFalse,以仅运行 Grover 电路,不进行额外的可视化。

Python 代码看起来是这样的:

def create_grover(oracleCircuit,amplifierCircuit,showstep):
    from qiskit import QuantumCircuit
    from math import sqrt, pow, pi
    groverCircuit = QuantumCircuit(qr,cr)
    # Initiate the Grover with Hadamards
    if showstep: display_circuit(groverCircuit,True,False)
    groverCircuit.h(qr)
    groverCircuit.barrier(qr)
    if showstep: display_circuit(groverCircuit,True,False)
    # Add the oracle and the inversion
    for n in range(int(pi/4*(sqrt(pow(2,
            oracleCircuit.num_qubits))))):
        groverCircuit+=oracleCircuit
        if showstep: display_circuit(groverCircuit,True,False)
        groverCircuit+=amplifierCircuit
        if showstep: display_circuit(groverCircuit,True,False)
    # Add measurements
    groverCircuit.measure(qr,cr)
    return(groverCircuit)

这里是我们刚刚看到的代码的步骤:

  1. 我们创建了一个量子电路,其中包含我们在创建 Oracle 时设置的全球量子寄存器和经典寄存器。

  2. 接下来,我们在所有量子比特上添加 H 门,并添加一个屏障指令以保持电路在编译时的完整性。这里有很多连续的重复门,我们需要保留所有这些门以确保电路能够工作。

  3. 现在是添加 Oracle 和放大器电路的关键步骤。

    为了从 Grover 电路中获得良好的结果,我们需要执行我们之前在关于经典搜索的快速插曲中讨论的正确数量的搜索。在量子 Grover 算法中,这表示为对每次搜索运行一次 Oracle 和放大器电路。

    对于大小为 N=2q 的数据库,最佳搜索次数或重复次数由以下公式确定:n。在这种情况下,q是我们电路中的量子比特数量。

    如果你做数学计算,你会发现对于一个 2 量子比特的 Grover 电路,只需要进行 1 次搜索就足够了(n=1.57)。对于 3 和 4 量子比特的电路,我们分别通过物理添加 Oracle 和放大器电路来增加 2 和 3 次重复。

    要了解为什么我们使用次重复,请参阅See also部分列出的讲座 22,4 月 11 日星期二:Grover文章中的如果我们运行 Grover 算法时间过长会发生什么?部分。

  4. 最后,我们将测量指令添加到电路中,现在电路可以运行了。

这些是我们构建 Grover 电路所需的三种函数。但如果你想的话,坐下来,尝试构建一组 Oracle 并测试它们在模拟器或真实的 IBM Quantum®量子计算机上;看看错误缓解对结果的影响,看看你的 Grover 代码实际上是什么样子,以及它在为 IBM Quantum®后端编译后变得有多大。

如何做到这一点...

要创建一个双量子比特的 Grover 电路,请按照以下步骤操作:

  1. 在你的 Python 环境中,运行ch9_r3_grover_main.py

  2. 当提示时,输入你想要找到的 Oracle 答案,形式为只包含 1 和 0 的两个数字字符串,例如,10

  3. 程序现在使用create_oracle()函数从ch9_grover_functions.py脚本中构建一个双量子位预言机电路,使用您输入的预言机类型,然后显示您创建的内容:

    …
    if size==2: 
            amplifierCircuit.cx(qr[size-2],qr[size-1]);
    …
    

    对于的双量子位预言机如下所示:

    图 9.10 – 为编码的预言机电路,用于

    图 9.10 – 为编码的预言机电路,用于

    我们正在构建一个电路,使用 CX 门来处理第二个量子位对第一个量子位的相位回弹。

  4. 现在构建两个量子位的放大器电路。

    下一步是使用create_amplifier()函数创建放大器电路。这也使用 CX 门进行相位回弹。该函数需要的唯一输入是量子位的数量。

    这是我们同时对所有量子位进行电路操作的地方,使用量子寄存器作为输入。例如,通过使用以下代码,我们在qr双量子位量子寄存器中的所有量子位上添加一个 Hadamard 门,即两个量子位:amplifierCircuit.h(qr)

    放大器电路及其相应的单位矩阵对于所有双量子位 Grover 电路都是相同的:

    图 9.11 – 双量子位放大器电路

    图 9.11 – 双量子位放大器电路

    如果需要,请回到创建放大器电路部分进行矩阵乘法的复习:

    如您所见,相位移位状态 10 已被放大到-1。当我们通过平方输出状态参数来计算结果概率,就像我们在第二章中做的那样,即使用 Python 进行量子计算和量子位,在比较比特和量子位部分,我们得到以下可能的输出。结果 10 = 100%,所有其他结果 = 0%。得到放大正确答案的概率是 100%。

  5. 逐步创建 Grover 电路:

    在下一步中,程序创建了一个包含我们的预言机和放大器的 Grover 量子电路,并添加了测量门。我们再次使用create_grover()函数来完成此操作。

    使用详细的电路创建显示,我们得到以下快速流程中的输出。让我们逐个查看电路。

    创建一个空白的双量子位电路:

    图 9.12 – 一个 100%概率得到结果的空白电路

    图 9.12 – 一个 100%概率得到结果的空白电路

    将两个量子位设置为叠加状态:

    图 9.13 – 具有两个量子位的叠加电路。每个结果的概率都是 25%:

    图 9.13 – 具有两个量子位的叠加电路。每个结果的概率都是 25%:,和

    添加预言机电路:

    ![图 9.14 – 添加 . 仍然有 25%的等概率得到每种结果: , , 和 ,但 现在相位移位

    ![img/Figure__9.14_B14436.jpg]

    图 9.14 – 添加 ![img/Formula_09_082.png] 的查询算子。仍然有 25%的等概率得到每种结果:![img/Formula_09_109.png],![img/Formula_008_013.png],和![img/Formula_09_110.png],但![img/Formula_008_013.png]现在相位移位为![img/Formula_09_018.png]

    添加放大器电路:

    ![图 9.15 – 添加放大器电路,该电路放大了相位移位结果的概率。现在有 100%的概率得到

    ![img/Figure__9.15_B14436.jpg]

    图 9.15 – 添加放大器电路,该电路放大了相位移位结果的概率。现在有 100%的概率得到 ![img/Formula_09_083.png]

    添加测量组件以完成电路。最终的电路将看起来像这样:

    ![图 9.16 – 三量子比特 Grover 电路,包含对 ![img/Formula_09_083.png] 的查询算子的一次重复和一个放大器

    ![img/Figure__9.16_B14436.jpg]

    图 9.16 – 三量子比特 Grover 电路,包含对 ![img/Formula_09_083.png] 的查询算子的一次重复和一个放大器

    如您所见,Grover 电路的第一步是使用 H 门将所有量子比特设置在偶数叠加状态。然后是查询算子和放大器电路。最后,我们添加所有量子比特的测量组件,以便我们可以读取最终结果。

  6. 现在,让我们运行电路并看看我们得到什么结果:图 9.17 – 在 Aer 模拟器上对 ![img/Formula_09_083.png] 查询算子的双量子比特 Grover 搜索结果

    图 9.17 – 在 Aer 模拟器上对 ![img/Formula_09_083.png] 查询算子的双量子比特 Grover 搜索结果

  7. 输入Y以在最不忙的 5 量子比特 IBM Q 后端运行 Grover 电路:![图 9.18 – 在 IBM Q 后端对 ![img/Formula_09_083.png] 查询算子的双量子比特 Grover 搜索结果

    ![img/Figure__9.18_B14436.jpg]

    图 9.18 – 在 IBM Q 后端对 ![img/Formula_09_083.png] 查询算子的双量子比特 Grover 搜索结果

    那么,这里发生了什么?为什么我们没有得到查询算子和放大器电路承诺的超精确结果?在模拟器中,我们得到正确结果的机会是 100%。现在我们下降到约 91%。我们能否使用误差缓解做得更好?对于双量子比特 Grover 电路,结果证明我们可以。

  8. 运行双量子比特 Grover 误差缓解以模拟实际量子计算机的行为。

    在这里,我们添加了在第八章“用 Ignis 清理我们的行为”中测试的缓解运行函数,该函数编码为mitigated_results(backend,circuit,results)函数:

    def mitigated_results(backend,circuit,results,
            results_sim):
        # Import the required classes
        from qiskit.providers.aer.noise import NoiseModel
        from qiskit.ignis.mitigation.measurement import
            (complete_meas_cal,CompleteMeasFitter)
        # Get noise model for backend
        noise_model = NoiseModel.from_backend(backend)
        # Create the measurement fitter
        qr = QuantumRegister(circuit.num_qubits)
        meas_calibs, state_labels = complete_meas_cal(
            qr=qr, circlabel='mcal')
        job = execute(meas_calibs,
            backend=Aer.get_backend('qasm_simulator'), 
            shots=8192, noise_model=noise_model)
        cal_results = job.result()
        meas_fitter = CompleteMeasFitter(cal_results, 
            state_labels, circlabel='mcal')
        print(meas_fitter.cal_matrix)
        # Get the filter object
        meas_filter = meas_fitter.filter
        # Results with mitigation
        mitigated_results = meas_filter.apply(results)
        mitigated_counts = mitigated_results.get_counts(0)
        return(mitigated_counts)
    

    显示了缓解后的结果:

    ![图 9.19 – 在 IBM Quantum®后端对 ![img/Formula_09_083.png] 查询算子的双量子比特 Grover 搜索结果的误差缓解结果

    ![img/Figure__9.19_B14436.jpg]

    图 9.19 – 在 IBM Quantum®后端上对或门的两个量子比特 Grover 搜索结果的错误缓解结果

    是的,这样更好;我们现在看到有 95%的几率得到正确的结果!这个电路的错误缓解提高了我们的结果,并减少了错误解决方案的概率。错误缓解之所以有效,是因为我们构建的电路相对较小。

  9. 查看最终的转换电路。

    在我们双量子比特 Grover 探索的最终步骤中,我们要查看在量子计算机上运行的转换电路。为此,我们重用了在第六章“你的量子电路究竟是什么样子”食谱中介绍的transpile_circuit(circuit,backend)函数,理解 Qiskit®门库

    def transpile_circuit(circuit,backend):
        from qiskit.compiler import transpile
        trans_circ = transpile(circuit, backend)
        display(trans_circ.draw(output="mpl"))
        print("Circuit data\n\nDepth: ",trans_circ.depth(),"\nWidth: ",trans_circ.width(),"\nSize: ",trans_circ.size())
    

图 9.20 – 双量子比特 Grover 电路的最终后端可执行量子电路

图 9.20 – 双量子比特 Grover 电路的最终后端可执行量子电路

下面是我们 Grover 电路的一些统计数据:

Depth:  9 
Width:  7 
Size:  15

我们的 Grover 电路从左到右的总深度为 9 次门操作;总共有 15 个单独的门。

对于双量子比特 Grover,我们实际上可以使用 100%的后端基本门,因此我们的转换电路的大小与我们的编码电路大致相同。对于三量子比特、四量子比特以及更多量子比特的 Grover 电路,我们将使用非基本门 CCX 和 MCX。参见“使用三量子比特 Grover 搜索”和“向 Grover 搜索添加更多量子比特”食谱,以查看这些内容。

还有更多……

关于我们最初使用的双量子比特 Grover 算法的简要说明。我们构建的双量子比特电路只是接近 Grover 算法的众多不同方法之一。我故意选择这种方法来减少我们使用的量子比特数量,并且在这个过程中,我们看到第二个量子比特扮演了两个角色:它是或门的组成部分,同时也是相位回弹组件的一部分。

构建电路的另一种方法是仅使用辅助量子比特严格进行相位回弹。这保持了或门量子比特的自由,但增加了一个量子比特,并稍微增加了电路的复杂性;我们现在必须使用 CCX 门来进行相位回弹,而不是 CX 门。

一个为编码并使用辅助量子比特构建的 Grover 电路的示例在这里:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter09/ch9_grover_ancilla.py

下面是如何构建这个 Python 示例的:

  1. 我们首先导入所需的类:

    from qiskit import QuantumCircuit, Aer, execute
    from IPython.core.display import display
    from qiskit.tools.visualization import plot_histogram
    
  2. 创建一个包含两个经典比特的三量子比特电路。

    我们将使用第三个,辅助量子比特作为相位回弹控制器:

    qc=QuantumCircuit(3,2)
    qc.h([0,1])
    qc.x(2)
    
  3. 添加或门的代码:

    qc.barrier([0,1,2])
    qc.x(0)
    qc.barrier([0,1,2])
    
  4. 现在我们使用辅助量子位添加相位回弹:

    qc.h(2)
    qc.ccx(0,1,2)
    qc.h(2)
    
  5. 我们完成了或门的编码:

    qc.barrier([0,1,2])
    qc.x(0)
    qc.barrier([0,1,2])
    
  6. 在我们完成之前,我们需要构建放大器:

    qc.h([0,1])
    qc.x([0,1])
    qc.h(1)
    qc.cx(0,1)
    qc.h(1)
    qc.barrier([0,1,2])
    qc.x([0,1])
    qc.h([0,1])
    
  7. 添加前两个量子位的测量。

    由于辅助量子位只是我们在电路内部使用的工具,我们不需要测量它:

    qc.measure([0,1],[0,1])
    
  8. 最后,我们显示电路,在模拟器上执行它,并显示结果:

    display(qc.draw('mpl'))
    backend = Aer.get_backend('qasm_simulator')
    job = execute(qc, backend, shots=1)
    result = job.result()
    counts = result.get_counts(qc)
    display(plot_histogram(counts))
    

或门电路看起来像这样:

![图 9.21 – 使用三个量子位和一个辅助量子位的或门 Grover 电路

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure__9.21_B14436.jpg)

图 9.21 – 使用三个量子位和一个辅助量子位的或门 Grover 电路

注意,为了使用额外的量子位扩展或门,你唯一要做的就是添加新的量子位在顶部,并扩展 CCX 和 CX 门以适应额外的相位回弹要求。

参见

要深入了解 Grover 算法,请参阅德克萨斯大学奥斯汀分校计算机科学 David J. Bruton 百周年教授 Scott Aaronson 的讲座系列中的第 22 讲,4 月 11 日星期二:Groverwww.scottaaronson.com/qclec/22.pdf

使用三量子位 Grover 进行搜索

三量子位 Grover 算法与我们之前配方中探索的两个量子位实现非常相似。主要区别在于我们如何为三个量子位而不是两个量子位构建或门电路,构建一个相位回弹,将相位添加到两个量子位而不是一个。

要做到这一点,我们必须使用一个受控-NOT 门,它使用两个量子位作为输入来翻转第三个量子位以纠缠量子位,并用相位标记正确答案。这个门是 Toffoli(CCX)门而不是 CX 门。

在以下示例中,两个量子位输入 Toffoli 门(CCX)带有 2 个控制量子位和 1 个受控量子位,作为相位回弹,如果三个量子位的值匹配正确答案,则将状态相位移

图 9.22 – 一个由 CCX 驱动的或门

图 9.22 – 一个由 CCX 驱动的或门

我们将使用与Grover 搜索算法配方中相同的示例函数。

如何做到这一点...

要创建一个三量子位 Grover 电路,请按照以下步骤进行:

  1. 在你的 Python 环境中运行ch9_r3_grover_main.py

  2. 当提示时,以三位字符串的形式输入你想要找到的或门答案,只包含 1 和 0,例如,100

  3. 程序现在将逐步构建你的三量子位 Grover,就像我们在前面的配方中构建的两个量子位 Grover 一样。我们将突出显示重要步骤,但不会深入任何细节。输出应该可以自说自话。

  4. 创建 Grover 电路。

    对于三个量子位,我们有总共 N = 8 ()种可能的结果,正如我们在构建 Grover 搜索算法配方中讨论的那样。

    对于大小为 N=2q 的数据库,最佳搜索次数或重复次数由以下公式确定:n 。对于 3 个量子比特,我们因此得到 n = 2.22。我们将这个值四舍五入到 2,以便对占卜器和放大器电路进行两次重复。

    最终电路将看起来像这样:

    图 9.23 – 用于占卜器的两个重复的三量子比特 Grover 电路

    图 9.23 – 用于占卜器的两个重复的三量子比特 Grover 电路

    如你所见,Grover 电路的第一步是使用 H 门将所有量子比特设置为偶数叠加。然后我们添加两次占卜器和放大器电路的重复。最后,我们添加所有量子比特的测量组件,以便我们可以读取最终结果。

    如果你将布尔 showsteps 参数设置为 True,你将看到 Grover 电路的各个步骤和临时结果,最终步骤看起来像这样:

    图 9.24 – 带有编码为

    图 9.24 – 带有编码为 的占卜器的三量子比特 Grover

    在这一最终步骤中,你可以看到正确答案,,已经被放大,现在有 ~94% 的概率(0.9722),而所有其他结果的概率都在 0.8%(-0.0882)的量级。

  5. 现在,让我们运行电路并查看我们得到什么结果:图 9.25 – 在模拟器上对占卜器的三量子比特 Grover 搜索结果

    图 9.25 – 在模拟器上对 占卜器的三量子比特 Grover 搜索结果

    注意结果如何很好地匹配我们从最终状态矢量预测的结果。

  6. 输入 Y 以在最不繁忙的五量子比特 IBM Quantum® 后端上运行 Grover 电路:图 9.26 – 在 IBM Quantum® 后端上对占卜器的三量子比特 Grover 搜索结果

    图 9.26 – 在 IBM Quantum® 后端上对 占卜器的三量子比特 Grover 搜索结果

    那么,这里发生了什么?为什么我们没有得到占卜器和放大器电路所承诺的超级精确的结果?使用模拟器,我们大约有 94% 的机会得到正确的结果。这里,我们显然下降到 ~40%。我们能使用错误缓解来做得更好吗?

  7. 查看最终的、缓解后的结果:图 9.27 – 在 IBM Quantum® 后端上对占卜器的三量子比特 Grover 搜索结果的错误缓解结果

    图 9.27 – 在 IBM Quantum® 后端上对 占卜器的三量子比特 Grover 搜索结果的错误缓解结果

    不行,这并没有解决问题。尽管结果略有改善,但错误缓解并没有解决这个问题。为什么?记住,基本的错误缓解主要关注测量误差,并且不考虑电路中可能出现的门问题。

    解释在于我们最终 Grover 电路的大小。让我们运行程序的最终步骤,它将最终电路转换为汇编代码,并为我们提供实际在后台运行的电路的大小、深度和宽度。

  8. 按下Enter键以查看最终、转换后的电路:图 9.28 – 3 量子比特 Grover 电路的最终后台可执行量子电路

    图 9.28 – 3 量子比特 Grover 电路的最终后台可执行量子电路

  9. 最后,我们得到了答案,电路大小:

    Circuit data
    Depth:  49 
    Size:  76
    

    我们 Grover 电路的总深度,从左到右,为 49 次门操作;总共有 76 个单独的门。如果你快速查看第八章用 Ignis 整理我们的行为,你会记得我们为每个量子比特的基础门提取了门错误率。尽管这些错误很小,在十分之一以下,当你以 100 次左右的顺序运行时,如前面的电路,很可能会出现错误。

    因此,在最终分析中,得到除预期之外的结果的略高概率主要是由于门错误,而不是测量错误。

向 Grover 搜索添加更多量子比特

到目前为止,我们在 Grover 编码方面做得相当不错。我们使用独特的 Qiskit®门为我们的电路包含的量子比特数量构建了两个和三个量子比特的电路:CX 和 CCX。对于四个量子比特及以上的 Grover,我们将使用多控制非门,MCX,以动态创建正确的控制输入数量。

准备工作

我们将使用与构建 Grover 搜索算法配方中相同的示例函数。

对于两个和三个量子比特的 Grover,我们可以使用预制 CX 和 CCX 门来创建我们的预言机和放大器电路。由于我们使用相同的模型来构建四个和更多量子比特的 Grover,而不是使用 CCCX、CCCCX 门等,我们在电路中使用 MCX 门,让 Qiskit®在幕后构建门逻辑。

注意

要理解我们在这里做什么,请回顾第六章理解 Qiskit®门库,特别是 CX 和 CCX 门。

这里是 CX 门的单位矩阵,正如在构建 Grover 搜索算法配方中看到的那样:

这里是 CCX 门(Toffoli)的单位矩阵,正如在使用 3 量子比特 Grover 搜索配方中看到的那样:

这里的魔法在于右下角,其中 1 已经离开了对角线,形成了一个小对角线。这种交换的效果是翻转最后一个量子比特的值。从代数上来说,这是一个简单的初始对角单位矩阵的操作;我们只需要交换最后两行。

控制控制的控制的非门(CCCX)的单位矩阵将看起来像这样:

img/Formula_09_104.png

结果表明,仅对几个量子比特构建表示 CCCX 的单位矩阵并不困难,但随着量子比特数量的增加,矩阵的大小会以 2^n 的速度增长。因此,下一个将是相当大的 CCCCX。

这都很不错,但我们现在该如何处理这个辉煌的矩阵呢?答案是让 Qiskit®通过使用 MCX 门将其编码成门。这个门接受一组控制量子比特和一个目标量子比特作为输入。你也可以指定使用辅助量子比特来处理相位回跳,但对我们这个电路,我们不会这么做。回顾一下“构建 Grover 搜索算法”配方中的“更多内容”部分的辅助样本。

在 Python 中使用该门看起来是这样的:

quantum_circuit.mcx([control qubits], target qubit)

要获取关于 MCX 门的更多帮助,请使用 Python help

>> help(QuantumCircuit.mcx)

在处理完 CCCX 门背后的细节后,构建 4+量子比特的 Grover 电路与两个和三个量子比特的 Grover 算法完全一样。

如何做到这一点...

在这里,我们将对覆盖和显示所有步骤的要求稍微宽松一些,因为 4 量子比特的 Grover 算法比我们之前使用的算法占用更多的空间。请随时回顾之前的配方以获取详细信息,并将它们应用于我们在这里构建的 Grover 算法。

要创建一个 4 量子比特的 Grover 算法,请按照以下步骤操作:

  1. 在你的 Python 环境中,运行ch9_r3_grover_main.py

  2. 当提示时,以仅由 1 和 0 组成的 3 位字符串的形式输入你想要找到的 Oracle 答案,例如,1000

  3. 让我们构建 Grover 电路。

    程序现在将逐步构建你的三量子比特 Grover 算法,就像我们在之前的配方中构建的两个量子比特 Grover 算法一样。我们将突出显示重要步骤,但不会深入细节。输出应该可以自说自明。

    对于三个量子比特,我们有总共 N = 16(img/Formula_09_107.png)种可能的结果,正如我们在“构建 Grover 搜索算法”配方中讨论的那样,电路的理想重复次数是![img/Formula_09_108.png]。对于 3 个量子比特,我们因此得到 n = 3.14。我们将它四舍五入到 3,以便 Oracle 和放大电路重复 3 次。

    最终电路将看起来像这样:

    ![图 9.29 – 具有三个重复的 4 量子比特 Grover 电路,用于 oracle img/Formula_09_102.png]

    图 9.29 – 具有三个重复的 4 量子比特 Grover 电路,用于 oracle img/Formula_09_100.png

    如果你将布尔参数showsteps设置为True,你将看到 Grover 电路的各个步骤和临时结果,最终步骤看起来如下:

    ![图 9.30 – 具有已编码 oracle 的 4 量子比特 Grover img/Formula_09_101.png]

    img/Formula_09_105.png]

    图 9.30 – 具有已编码 oracle 的 4 量子比特 Grover img/Formula_09_106.png

    在这个最终步骤中,你可以看到(经过一些挖掘后),正确的答案![img/Formula_09_103.png]已经被放大(-0.98046875),现在有大约 96%的概率,而所有其他结果都有大约 0.02%的概率。

  4. 现在,让我们运行电路并看看我们得到什么结果:![图 9.31 – 在模拟器上对 oracle 进行四量子比特 Grover 搜索的结果 图片

    ![图 9.31 – 在模拟器上对 oracle 进行四量子比特 Grover 搜索的结果

  5. 输入 Y 以在最不繁忙的五量子比特 IBM Quantum® 后端运行 Grover 电路:![图 9.32 – 在 IBM Quantum® 后端上对 oracle 进行四量子比特 Grover 搜索的结果 图片

    ![图 9.32 – 在 IBM Quantum® 后端上对 oracle 进行四量子比特 Grover 搜索的结果

    这次,我们似乎什么都没有得到,只有噪音。当然,在那随机性中没有任何明确的答案。正确的答案根本不在列表的顶部。让我们看看错误缓解后的结果。

    解释再次在于我们最终 Grover 电路的大小。程序的最终步骤将给我们编译后的电路。

  6. 按下 Enter 键以查看最终、编译后的电路。这次的结果相当令人印象深刻:

![图 9.33 – 4-量子比特 Grover 电路的最终后端可执行量子电路图片

![图 9.33 – 4-量子比特 Grover 电路的最终后端可执行量子电路再次,我们得到了答案,电路大小:pyCircuit dataDepth:  311 Size:  409我们的 Grover 电路总深度,从左到右,为 311 次门操作;总共有 409 个单独的门。同样,就像在 使用三量子比特 Grover 搜索 的配方中一样,我们只得到噪音是因为门错误,而不是测量错误。电路太大,无法在 NISQ 机器上高效执行。我们的 Grover 电路没有问题,只是太大!## 还有更多……现在继续创建一个五量子比特的 Grover 电路,看看它会带你去哪里。模拟器应该能够勇敢地处理它,并给出预期的结果,比如对于一个 oracle。但在真实的量子计算机上运行它只会给你噪音:

![图 9.34 – 在模拟器上对 oracle 进行五量子比特 Grover 搜索的结果图片

![图 9.34 – 在模拟器上对 oracle 进行五量子比特 Grover 搜索的结果

在真实量子计算机上运行五量子比特 Grover 电路的最终结果显示:

![图 9.35 – 在 IBM Quantum® 后端上对 oracle 进行五量子比特 Grover 搜索的结果图片

![图 9.35 – 在 IBM Quantum® 后端上对 oracle 进行五量子比特 Grover 搜索的结果

查看最终、编译后的电路。根据您在哪个 IBM Quantum® 机器上运行电路,您可能会得到不同的大小和深度,这取决于机器拓扑结构。在一个只有五个量子比特的机器上创建一个 CCCCX 门(使用 MCX 门)将需要大量的交换,更不用说重复 oracle 和放大器四次了。

ibmqx2 五量子比特后端运行时的电路大小示例:

Circuit data
Depth:  830 
Size:  1024

这个大小的电路将不得不等待通用量子计算机;它太大,无法在 NISQ 机器上成功运行。

在你的代码中使用 Grover 电路

ch9_r3_grover_main.pych9_grover_functions.py脚本中有很多代码。如果你只是想运行 Grover,例如,想在另一个 Python 程序中利用它,那么交互式主程序是不需要的。

准备工作

你需要的示例 Grover 函数代码包含在这里:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter09/ch9_grover_functions.py.

如何做到这一点...

  1. 技术上,你只需要在你的代码中包含以下简短的代码片段:

    from ch9_grover_functions import *
    oracle=create_oracle("01",2)
    amplifier=create_amplifier(2)
    grover=create_grover(oracle,amplifier,False)
    print(grover)
    
  2. 上一段代码应该得到以下结果:

图 9.36 – 在最小脚本中使用 Grover 函数创建一个 Grover 电路

图 9.36 – 在最小脚本中使用 Grover 函数创建一个 ![img/Formula_09_108.png] Grover 电路

从 Qiskit Terra 电路的角度来看,我们现在已经完成了,你可以将你的grover电路与你的混合经典/量子代码一起包含,以获取 Grover 搜索结果。

还有更多...

但我们可以做得更好!Qiskit Aqua 实际上包含一个可以直接使用而不需要编写任何代码的Grover()函数。更多关于这一点,请参考第十章中的使用 Aqua 了解算法食谱,了解 Aqua 算法

第十章:第十章:通过 Aqua 了解算法

因此,我们终于来到了这部分,我们将稍微放慢自己编写电路的速度,转而看看 Qiskit®中最有趣的部分——Qiskit Aqua。

在我们上一章构建各种 Grover 实现时,我们看到了这个看似简单的算法在用 Qiskit Terra 实现时如何变成一个难以驾驭的编码怪物。如果你正在构建一个混合经典/量子程序(你只是想使用 Grover 搜索功能),如果你能直接导入并运行 Qiskit®的实现,或者其他量子算法,而不必从头编写代码,那将会简单得多。

因此,伴随着一阵欢呼和鼓声,我们现在初步了解一下几个 Qiskit Aqua 算法(Grover 和 Shor),你可以直接使用它们。

本章我们将涵盖以下菜谱:

  • 将 Grover 算法作为 Aqua 函数运行

  • 将 Shor 算法作为 Aqua 函数运行

  • 探索更多 Aqua 算法

当我们完成这里的工作后,你将了解到如何将 Qiskit Aqua 算法集成到自己的代码中,并将知道在哪里找到更多算法进行测试,以及如何开始探索算法实际上是如何在 Qiskit®代码中使用 Python 构建的。

技术要求

本章中我们讨论的量子程序可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/tree/master/Chapter10.

将 Grover 算法作为 Aqua 函数运行

第九章的结尾,我们承诺将有一个更简单的方法将 Grover 搜索算法包含到你的量子电路中。在这个菜谱中,我们将达到相同的结果,但无需从头开始构建电路;Qiskit Aqua 将设置预言机,构建 Grover 电路,并为我们运行它。

就像导入和调用 Python 类和方法来处理各种事情一样,例如使用from math import pi来获取 的数值表示,你还可以使用 Qiskit®组件。为什么还要重新发明轮子,构建自己的 Grover 搜索算法实现,当它已经包含在 Qiskit®中时?

准备工作

本菜谱的示例代码可以在这里找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter10/ch10_r1_grover_aqua.py.

在我们深入之前,让我们看看 Grover Aqua 算法接受以定义预言机的两种输入形式:

  • LogicalExpressionOracle 输入类型

  • TruthTableOracle 输入类型

逻辑字符串

第九章Grover 搜索算法”中,我们使用了针对 的逻辑表达式查询器示例,该查询器在 The two-bit Grover 菜谱中使用,其中最低有效位(LSB)位于逻辑的左侧:

'~A & B'

这实际上翻译成:非 A 和 B,然后可以翻译成我们的 Dirac 矢量表示法,即第一个量子比特(A)= 0 和第二个量子比特(B)=1,或

位字符串

当您使用真值表查询器输入时,您创建一个表示查询器预期输出的位字符串。对于 的示例,位字符串如下:

'0010' 

我们在第二章使用 Python 进行量子计算和量子比特的快速入门”的菜谱中简要介绍了这种表示法。如果您需要复习,请查看该内容。

如何操作...

为了处理 Grover 算法的创建和运行,我们在 ch10_r1_grover_aqua.py 脚本中创建了四个函数。在我们继续测试之前,让我们先看看这些函数:

示例代码

  1. 首先,我们导入所需的类和方法,并设置一些全局变量:

    from qiskit import Aer, IBMQ
    from qiskit.aqua.algorithms import Grover
    from qiskit.aqua.components.oracles import LogicalExpressionOracle, TruthTableOracle
    from qiskit.tools.visualization import plot_histogram
    from IPython.core.display import display
    global oracle_method, oracle_type
    
  2. log_length(oracle_input,oracle_method) 函数接受查询器输入(逻辑或二进制)和查询器方法(逻辑表达式或位字符串)作为输入,并返回 Grover 电路需要包含的理想迭代次数。如果查询器输入是逻辑字符串,我们首先通过计算字符串中的字母数量来计算量子比特的数量,不包括波浪号 (~)、和号 (&) 和空格:

    def log_length(oracle_input,oracle_method):
        from math import sqrt, pow, pi, log
        if oracle_method=="log":
            filtered = [c.lower() for c in oracle_input if             c.isalpha()]
            result = len(filtered)
            num_iterations=int(pi/4*(sqrt(pow(2,result))))
        else:
            num_iterations = int(pi/4*(sqrt(pow(2,
                log(len(oracle_input),2)))))
        print("Iterations: ", num_iterations)
        return num_iterations
    
  3. create_oracle(oracle_method) 函数接受查询器方法作为输入,并提示输入查询器逻辑表达式或位字符串。从输入中,它调用 log_length(oracle_input,oracle_method) 函数,该函数根据 公式计算所需的迭代次数。

    在 Python 中,它看起来是这样的:

    def create_oracle(oracle_method):
        oracle_text={"log":"~A & ~B & C","bit":"00001000"}
        # set the input
        global num_iterations    
        print("Enter the oracle input string, such         as:"+oracle_text[oracle_method]+"\nor enter 'def'        for a default string.")
        oracle_input=input('\nOracle input:\n ')
        if oracle_input=="def":
            oracle_type=oracle_text[oracle_method]
        else:
            oracle_type = oracle_input
        num_iterations=log_length(oracle_type, oracle_method)
        return(oracle_type)
    
  4. create_grover(oracle_type) 函数接受 oracle_type 字符串作为输入,例如,~A&B,并使用 Grover(LogicalExpressionOracle (oracle_type),num_iterations=num_iterations) 函数创建具有适当迭代次数的算法。

    在 Python 中,它将如下所示:

    def create_grover(oracle_type, oracle_method):
        # Build the circuit
        if oracle_method=="log":
            algorithm = Grover(LogicalExpressionOracle(
                oracle_type),num_iterations=num_iterations)
            oracle_circuit = Grover(LogicalExpressionOracle(
                oracle_type)).construct_circuit()
        else:
            algorithm = Grover(TruthTableOracle(
                oracle_type),num_iterations=num_iterations)
            oracle_circuit = Grover(TruthTableOracle(
                oracle_type)).construct_circuit()
        display(oracle_circuit.draw(output="mpl"))
        display(algorithm)
        return(algorithm)
    
  5. run_grover(algorithm,oracle_type) 函数接受我们刚刚创建的算法作为输入,首先在本地 Aer 模拟器上运行,然后在最不繁忙的具有五个量子比特的 IBM Quantum®后端上运行。

    这是我们如何在 Python 中构建它的方法:

    def run_grover(algorithm,oracle_type,oracle_method):
        # Run the algorithm on a simulator, printing the most     # frequently occurring result
        backend = Aer.get_backend('qasm_simulator')
        result = algorithm.run(backend)
        print("Oracle method:",oracle_method)
        print("Oracle for:", oracle_type)
        print("Aer Result:",result['top_measurement'])
        display(plot_histogram(result['measurement']))
        # Run the algorithm on an IBM Q backend, printing the     # most frequently occurring result
        print("Getting provider...")
        if not IBMQ.active_account():
            IBMQ.load_account()
        provider = IBMQ.get_provider()
        from qiskit.providers.ibmq import least_busy
        filtered_backend = least_busy(provider.backends(        n_qubits=5,, operational=True, simulator=False))
        result = algorithm.run(filtered_backend)
        print("Oracle method:",oracle_method)
        print("Oracle for:", oracle_type)
        print("IBMQ "+filtered_backend.name()+        " Result:",,result['top_measurement'])
        display(plot_histogram(result['measurement']))
        print(result)
    
  6. main() 函数提示输入查询器方法,然后创建查询器并运行 Grover 算法:

    def main():
        oracle_method="log"
        while oracle_method!=0:
            print("Ch 11: Grover search with Aqua")
            print("------------------------------")    
            # set the oracle method: "Log" for logical         # expression or "Bit" for bit string. 
            oracle_method = input("Select oracle method (log             or bit):\n")
            type=create_oracle(oracle_method)
            algorithm=create_grover(type, oracle_method)        run_grover(algorithm,type, oracle_method)
    

运行代码

要创建和运行一个使用逻辑表达式作为输入的 Aqua 生成的 Grover 电路,请按照以下步骤操作:

  1. 在您的 Python 环境中,运行 ch10_r1_grover_aqua.py

  2. 当提示时,通过输入 log 选择逻辑表达式查询器方法。

    你可以自由地通过输入 bit 来测试使用比特串输入相同的逻辑门。

  3. 现在输入一个与上一个例子类似的逻辑表达式:

    ~A & B
    

    如果你读出逻辑表达式,你会得到 NOT A AND B,这对应于 公式。记住,A 是最低有效位,在 Qiskit® 中对应于密钥表示法中的最右边数字。

    对于比特串逻辑门,输入将是 0010

  4. 逻辑门输入和最佳迭代次数会显示出来,同时还有 Aqua 创建的逻辑门电路:图 10.1 – 为 公式 创建的 Aqua 逻辑门电路

    图 10.1 – 为 公式 创建的 Aqua 逻辑门电路

  5. 现在逻辑门运行在本地 Aer 模拟器上,结果如下所示:![图 10.2 – 在你的本地 Aer 模拟器上运行 公式 逻辑门的结果

    图片

    图 10.2 – 在你的本地 Aer 模拟器上运行 公式 逻辑门的结果

  6. 逻辑门现在运行在最不繁忙的 IBM Quantum® 后端,结果如下所示:

![图 10.3 – 在 IBM Quantum® 后端运行 公式 逻辑门的结果

图片

图 10.3 – 在 IBM Quantum® 后端运行 公式 逻辑门的结果

它是如何工作的...

如果你查看 ch10_r1_grover_aqua.py 程序,你会找到大约 100 行代码。其中大部分不是运行 Grover 算法所必需的。假设你从某处获得了逻辑表达式或比特串逻辑门输入,你只需四行代码就可以运行它:

In [1]: from qiskit import Aer
In [2]: from qiskit.aqua.algorithms import Grover
In [3]: from qiskit.aqua.components.oracles import LogicalExpressionOracle
In [4]: Grover(LogicalExpressionOracle("~A&B")).run(backend)["top_measurement"]
Out[5]: '10'

前三行代码导入所需的类。这第四行代码创建并运行了 Grover 电路,以逻辑表达式逻辑门作为输入 – 在这种情况下,~A&B,来表示 公式,并提取出逻辑门编码的顶部测量结果,即获胜者。

更多内容...

再次,就像使用 Terra 构建的电路一样,当你从两个量子位增加到三个量子位时,事情就会发生。让我们看看。要查看最终编译后的电路(在 IBM Quantum® 后端运行)的外观,你可以登录到 IBM Quantum Experience® 来查看你运行的全详细结果:

  1. 前往以下 URL,然后使用你的 IBM Quantum Experience® 账户登录:quantum-computing.ibm.com/

  2. 欢迎 面板中,向下滚动到 最新结果

  3. 定位你刚刚运行的工作,并点击它:![图 10.4 – 在 IBM Quantum Experience® 中 IBM Quantum® 后端运行 公式 逻辑门的工作结果

    图片

    图 10.4 – 在 IBM Quantum Experience® 中 IBM Quantum® 后端运行 公式 逻辑门的工作结果

  4. 比较电路。

    通过查看这里的作业,你可以比较你在 第九章Building Grover's search algorithm 菜单中运行的电路大小,以及你的新 Aqua 创建的电路:

    ![Figure 10.5 – 对于 的 2 量子比特 Aqua Grover 与你的 Terra 构建的 2 量子比特 Grover]

    ](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_10.5_B14436.jpg)

    Figure 10.5 – 对于 的 2 量子比特 Aqua Grover 与你的 Terra 构建的 2 量子比特 Grover

    对于双量子比特电路,其大小与你在 Terra 构建的电路中看到的大小相对应。从差异中你可以看到,Aqua 构建的电路使用一个辅助量子比特来处理相移机制。这使得电路稍微大一些,Qiskit Aqua Grover 的深度为 15,而 Qiskit Terra Grover 的深度为 9。大小也从 15 增长到 24。

  5. 现在,运行一个 3 量子比特的 Qiskit Aqua 创建的 Grover 并查看结果。

    再次运行 ch10_r1_grover_aqua.py 程序,选择日志预言机类型,并使用以下逻辑字符串为 编码:~A & ~B & C

    Qiskit Aer 运行的 Grover 的结果应该看起来像以下这样:

![Figure 10.6 – Aer 对 Aqua 创建的 Grover 的结果]

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_10.6_B14436.jpg)

Figure 10.6 – Aer 对 Aqua 创建的 Grover 的结果

在 IBM Quantum® 后端上运行的 Grover 的结果应该看起来像这样:

![Figure 10.7 – IBM Quantum® 对 Aqua 创建的 Grover 的结果]

](https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/qnt-comp-prac/img/Figure_10.7_B14436.jpg)

Figure 10.7 – IBM Quantum® 对 Aqua 创建的 Grover 的结果

看看这些结果。它们看起来不像我们在 第九章Grover's Search Algorithm 中的 The three-qubit Grover 菜单中得到的 3 量子比特结果,而更像我们在 Adding more qubits to the Grover search 菜单中看到的杂乱的 4+ 量子比特结果。正如我们在那个菜谱中讨论的,我们已经明显超出了 NISQ 机器的电路深度,从实验中得到的噪声比信号多。

那么,这算是有趣的吗?我们看到了 Qiskit Aqua 如何编码我们在 第九章Grover's Search Algorithm 中花费了大量时间构建的相同算法。但 Qiskit Aqua 包含了更多内容。

我们只是触及了表面。如果你花了一些时间回顾量子信息的历史,你可能已经挑选出了一些喜欢的算法;其中之一很可能是 Grover 算法,但最著名的一个可能是 Shor 算法,这就是下一个要讨论的内容。继续阅读!

将 Shor 算法作为 Aqua 函数运行

量子计算算法中的另一位真正的光辉人物是彼得·肖尔(Peter Shor)的算法,该算法可以追溯到 1984 年,其中他证明了在足够强大的量子计算机的帮助下,你可以对非常大的整数进行质因数分解。这不仅从学术角度来看很重要,而且例如,将非常大的(数千位)数字分解为其构成质数是今天用于确保在线交易(从银行和社交媒体到你车中的计算机)的 RSA 加密的核心。

当这些足够大的量子计算机进入舞台时,理论上可以在几分钟内破解那些需要数周、数月、数年甚至更长时间才能破解的加密密钥。

为了调整我们的期望,在今天的 NISQ 机器上运行肖尔算法更多的是一种学术兴趣。正如你将注意到的,肖尔电路即使在相对较小的分解练习中也会变得相当大,正如你在前面的章节中看到的,随着电路的增长,错误也会增加。

通过 IBM Quantum Experience®,你可以访问到 15 个量子比特大小的量子计算机和 32 个模拟器。正如你将看到的,量子比特的数量限制了我们可以分解的数字的大小。

最后要考虑的一个问题是模拟的规模。使用量子计算机的巨大理论优势是它们有可能在某些问题上有指数级的加速。另一方面,随着每个额外量子比特的增加,模拟量子计算机会变得指数级困难。

当我们运行用于更大数字的肖尔算法时,这里所说的“更大”是指一旦我们超过 63 并向上,你的本地模拟器将开始抱怨,是时候切换到 IBM Quantum®提供的模拟器了。

查看以下表格,以获取一些建议的测试数字:

表 10.1 – 小于 40 的数的质因数分解

表 10.1 – 小于 40 的数的质因数分解

小贴士

要了解更多关于分解的乐趣,请参阅www.mymathtables.com/numbers/one-hundred-factor-and-prime-factor-table.htmlwww.mathsisfun.com/prime-factorization.html

Qiskit Aqua 肖尔算法接受大于 1 的奇数作为输入,因此表格只列出了这些数字。列出的作为质数的数字本身不能被分解,但其余的都可以测试。测试其中的一些。

准备工作

该菜谱的示例代码可以在以下链接找到:github.com/PacktPublishing/Quantum-Computing-in-Practice-with-Qiskit-and-IBM-Quantum-Experience/blob/master/Chapter10/ch10_r2_shor_aqua.py

如何做到这一点…

为了处理 Grover 算法的创建和运行,我们将在 ch10_r2_shor_aqua.py 脚本中构建三个函数。首先,让我们检查代码,然后运行它。

样本代码

  1. 首先,我们导入所需的类和方法:

    from qiskit import Aer, IBMQ
    from qiskit.aqua.algorithms import Shor
    import time
    
  2. display_shor(N) 函数接收一个整数作为输入,并使用 Shor() 方法构建和显示短路以及电路数据:

    def display_shor(N):
        print("Building Shor circuit...")
        shor_circuit = Shor(N=N).construct_circuit()
        print(shor_circuit)
        print("Circuit data\n\nDepth: ",shor_circuit.        depth(),"\nWidth: ",shor_circuit.width(),"\nSize:        ",shor_circuit.size())
    
  3. run_shor(N) 函数接收一个整数作为输入,创建 Shor 电路,并在本地模拟器上运行它。函数随后显示运行结果:

    def run_shor(N):
        if N<=64: #Arbitrarily set upper limit for local               #simulator    
            print("Getting local simulator backend...")
            backend = Aer.get_backend('qasm_simulator')
        else:
            print("Getting provider...")
            if not IBMQ.active_account():
                IBMQ.load_account()
            provider = IBMQ.get_provider()
            print("Getting IBM Q simulator backend...")
            backend = provider.get_backend(
                'ibmq_qasm_simulator')
        print("Running Shor's algorithm for",str(N),"on",        backend,"...")
        results=Shor(N=N).run(backend)
        print("\nResults:")
        if results['factors']==[]:
            print("No prime factors: ",str(N),"=",str(N))
        elif isinstance(results['factors'][0],int):
            print("Prime factors: ",str(N),"=",
                results['factors'][0],"^ 2")
        else:
            print("Prime factors: ",str(N),"=",
                results['factors'][0][0],"*",
                results['factors'][0][1])
    
  4. main() 函数处理提示并验证一个大于 1 的奇数输入,然后运行前面的函数。使用开始和结束时间来测量构建电路和运行它所需的时间:

    def main():
        number=1
        print("\nCh 11: Shor's algorithm with Aqua")     
        print("---------------------------------")   
        while number!=0:
            number=int(input("\nEnter an odd number N >1 (0
                to exit):\n"))
            if number>1 and number % 2>0:
                type=input("Enter R to run the Shor
                    algorithm, D to display the circuit.\n")
                start_time=time.time()
                if type.upper()=="D":
                    display_shor(number)
                elif type.upper()=="R":
                    run_shor(number)
                elif type.upper() in ["RD","DR"]:
                    display_shor(number)
                    run_shor(number)
                end_time=time.time()
                print("Elapsed time: ","%.2f" % (
                    end_time-start_time), "s")
            else:
                print("The number must be odd 
                    and larger than 1.")
    

运行代码

  1. 在你的环境中运行 ch10_r2_shor_aqua.py

  2. 当提示时,输入 N,一个大于 1 的奇数。

    这是我们要进行素数因子分解的数字。首先,尝试以下三个数字:5915

  3. 算法返回三种结果之一:

    没有素数因子:如果你输入的数字是一个不能分解的素数,例如:

    图 10.8 – 使用 5 作为输入的 Shor 算法的结果

    图 10.8 – 使用 5 作为输入的 Shor 算法的结果

    平方因子分解:如果数字可以表示为素数乘以自身的因子分解:

    图 10.9 – 使用 9 作为输入的 Shor 算法的结果

    图 10.9 – 使用 9 作为输入的 Shor 算法的结果

    两个素数因子分解:如果数字可以表示为两个不同素数的因子分解。这是我们想要的结果:

    图 10.10 – 使用 15 作为输入的 Shor 算法的结果

    图 10.10 – 使用 15 作为输入的 Shor 算法的结果

  4. 现在尝试使用更大的数字运行算法,并观察构建和执行电路所需的时间如何增加。

    你会注意到,随着数字的增大,你的本地模拟器越来越难以跟上。在我的工作站(Apple iMac,16 GB RAM)上,当数字超过 63 时,我就没有足够的内存来构建电路了。run_shor(N) 函数代码内置了一个断点,在 64 时切换到 IBM Quantum® 模拟器后端。

    如果你想测试本地机器的性能,可以随意移动本地/IBM Quantum® 断点。记住,IBM Quantum® 模拟器后端运行在 IBM POWER9™ 服务器上,拥有相当大的计算能力!

更多内容…

就像我们在 将 Grover 算法作为 Aqua 函数运行 菜单中讨论的那样,你只需几行代码就可以运行 Shor 函数:

In [1]: from qiskit import Aer
In [2]: from qiskit.aqua.algorithms import Shor
In [3]: backend = Aer.get_backend('qasm_simulator')
In [4]: results=Shor(N=15).run(backend)
In [5]: results['factors']

在这个例子中,我们为数字 15 运行 Shor 算法。运行此代码示例应产生以下输出:

Out[5] [[3, 5]]

我们到目前为止所做的工作只是运行了 Shor 算法,使用一个输入参数N——你想要分解的整数。默认情况下,如果你运行Shor()而没有输入,它将默认为 15,这是可以分解的最小的非平凡整数。请从前面的表中验证这一点。

Shor 函数可以选择性地接受另一个输入参数a,它是一个小于N的互质数,并且最大公约数为 1:

In [4]: results=Shor(N=15, a=2).run(backend)

默认情况下,a被设置为2,对于我们在这里玩的小型整数,这可能不会有太大影响,但请随意实验。

参考信息

  • Scott Aaronson 在他的博客上有一篇关于 Shor 算法的精彩文章:Shor. I'll do it,链接为www.scottaaronson.com/blog/?p=208

  • 要了解 Shor 算法在 Python 和 Qiskit®上的详细概述,请参阅 Dr Christine Corbett Moran 所著的《Mastering Quantum Computing with IBM QX》的第十二章,标题为“Shor 算法”。

  • 直接从 Qiskit®教科书中,以下是 Shor 算法在 Python 和 Qiskit®中的分解:qiskit.org/textbook/ch-algorithms/shor.html

探索更多 Aqua 算法

我们现在已经到达了本书的结尾,也是我们共同探索 Qiskit®之旅的结束。在我们的旅途中,我们查看了一些基本的量子编程,并探索了 IBM Quantum®后端——实际的量子计算机!我们在这些计算机上运行了我们的程序,并得到了量子结果。

我们开始深入研究量子计算的核心,即算法。然而,这本书并不是关于算法的;我们只是用一些非常基本的概念来探讨量子算法与它们的经典对应物之间的差异,以及编写它们可能的感觉。

在讨论了如何编写算法以及与经典算法相比有时反直觉的求解方法之后,我们还研究了 Qiskit Aqua 中包含的预制算法:Grover 算法和 Shor 算法。我喜欢将 Qiskit®的这一部分视为量子计算的应用商店。

当你遇到可能需要量子计算解决方案的问题,但你并不一定想自己编写算法时,你会去这里。就像大多数人不会自己编写程序来获取天气预报一样;他们只是使用现成的天气应用程序。

准备工作

Qiskit Aqua 不仅包含 Grover 算法和 Shor 算法,IBM Quantum®团队以及全球的合作伙伴们正在通过实现和纯算法来填充它,这些算法针对的是近期有潜力的领域,同时也为量子计算机在强度和能力增强后更远的实现做准备。

首先,Qiskit Aqua 组件包括一个qiskit.aqua.algorithms包。

其中包括我们测试过的 Grover 和 Shor 算法,但还有其他特定的算法,如 QSVM(量子支持向量机)、VQE(变分量子本征值求解器算法)等。探索这个库,了解如何在今天可用的后端上探索各种算法,以及如何为未来通用的量子计算机扩展它们:

  • qiskit.chemistry) 允许你使用你喜欢的建模工具在分子上进行能量计算实验。

  • qiskit.finance) 包含一系列以伊辛哈密顿量形式结构化的函数,应用于金融模型。

  • qiskit.ml) 包含样本集。你可以使用这些 Aqua 分类器和 SVM 算法。

  • qiskit.optimization)。此模块包含具有特定算法、应用、问题等几个子模块。

如何做到这一点...

我们不会深入探讨这些特定的 Qiskit Aqua 模块,但为了开始探索算法,你可以遵循这个路径示例:

  1. 导入你感兴趣的包,例如,如果你想探索化学,这个:

    import qiskit
    from qiskit.chemistry import *
    
  2. 然后查看内置文档。

    Qiskit® 提供了卓越的 Python 帮助,你可以从这里开始你的探索;例如:

    help(qiskit.chemistry)
    
  3. 探索 Qiskit® 的一般资源,以进一步探索你感兴趣的研究领域。

    例如,这里 Qiskit® 的教程:qiskit.org/documentation/tutorials/chemistry/index.html

    例如,在 使用 Qiskit 学习量子计算 教科书中:qiskit.org/textbook/ch-applications/vqe-molecules.html

  4. 最后,进行实验!

    学习各种算法,并将它们整合到你自己混合经典/量子 Python 代码中,以满足你的需求,就像我们在本书的各个食谱中做的那样。

我们还有许多尚未触及的 Qiskit® 功能,例如在你的电路中混合布尔逻辑,直接用 OpenPulse 编程量子比特,更高级的错误模拟等等。所有这些功能,以及更多,都可供你探索。如果你不想独自探索,四处看看,看看你能否在你所在的地区找到一些量子计算聚会或研讨会。

Qiskit Slack 频道 qiskit.slack.com 是你量子社交探索的一个绝佳起点。从 IBM Quantum Experience® 的支持页面注册:quantum-computing.ibm.com/support

更多

Qiskit Aqua 算法并非凭空出现;有人编写了它们,并将它们添加到集合中。Qiskit® 是开源的,由不断增长的开放源代码贡献者群体构建。你呢?

即使你并没有计划成为 Qiskit®的贡献者,也请继续探索源代码。如果你在本地安装了 Qiskit®,源代码就在你的指尖。如果你使用建议的 Anaconda 安装,你的 Qiskit®源可能位于类似以下的位置(以下为 macOS 示例):

/Users/<your_user_name>/opt/anaconda3/envs/<your_environment>/lib/python3.7/site-packages/qiskit/

在构建了我们整本书中使用的特性和功能的函数和类中四处看看。谁知道呢,也许你能想出一个更好的方式来展示量子比特状态,或者提出一个全新的算法,然后将其贡献回 Qiskit®。

关于如何为 Qiskit®做出贡献的信息,请在此处查看:qiskit.org/documentation/contributing_to_qiskit.html

参考信息

今天的算法开发在很大程度上仍然是理论性的,因为我们可以在目前有限的 NISQ 机器和量子模拟器上成功运行它们,但我们无法访问拥有数百或数千个量子比特的后端。记住,模拟器的大小会随着量子比特数量的指数增长;在这些电路大小上运行模拟器极具挑战性。

在未来并非不可预见的时刻,这可能会发生变化,至少如果 IBM Quantum®能够如愿以偿的话。2020 年 9 月初,IBM Quantum 的 Jay Gambetta 在年度 IBM Quantum Summit 上展示了 IBM 的路线图。这是一个大胆的计划,预计到 2023 年底将达到 1,121 个物理量子比特。有了这么多物理量子比特,将能够真正开始探索错误纠正量子比特,正如在第八章中“通过 Exploring your qubits to understand T1, T2, errors, and gates”食谱所描述的,用 Ignis 清理你的量子行为。所以请密切关注这个领域。

这是路线图文档:IBM 的量子技术扩展路线图,2020 年 9 月 15 日,Jay Gambetta:www.ibm.com/blogs/research/2020/09/ibm-quantum-roadmap/

感谢!

因此,亲爱的读者,你已经跟随我走到了这里,或者至少你已经翻到了最后一章的最后一页,看看故事是如何结束的...剧透警告:这是一个悬念!

量子计算仍然处于初级阶段,你在本书中参与构建的食谱,虽然为你进一步探索指明了方向,但不足以让你自信地确立作为量子计算程序员的职业生涯;这需要时间和努力。

就像一门 C 编程基础课程可能会引导你走上通过构建下一个社交媒体现象来致富的道路一样,你现在所接触到的这种基础尝试也可能带来同样的效果。现在你已经拥有了这些,那就大胆地跳进去吧,成为 Qiskit 倡导者,咨询你的大学或学院提供的相关课程和项目,规划你作为量子计算开发人员或研究人员的职业道路,或者为什么不就在你的车库里开始下一个大型的量子初创公司呢?

玩得开心!

posted @ 2025-10-27 08:51  绝不原创的飞龙  阅读(2)  评论(0)    收藏  举报