*There are much many text of Latex Mathtype in this document,yet it is hard for Github to render these Mathtype.So I strongly recommend you to browse this html page : HTML for a perfect experience.*

写给开发者的量子计算入门教程

--- 基于Q#语言描述

 

 

前言

写作这份文档时,我只是一个对计算机、对量子计算充满好奇心、有着极大兴趣的人。从始至终,我深知那些深奥的物理定理和数学分析对初学者而言意味着怎样的难度和挑战,因此,我写这份文档的目的就是希望它能稍稍降低量子计算的门槛,如果我的创作真的帮助到了你,那么我将十分荣幸。

在这份文档中,我不会涉及过多和过深的物理学、量子力学以及数学等相关内容,但希望读者能够有一定的线性代数和概率论的基础,这个基础不需要多深,只要了解过基本的矩阵、向量、空间等知识即可。为了达到降低门槛通俗易懂的目的,我极力地削减了文档中与数学及其他学科有关的内容,但是还是有一些内容不得不引入一些数学计算、证明等,但是我不希望你纠结于这些内容,作为开发者,底层的东西对我们而言应该是透明的,我们只需要知道它是什么,给它一个输入它会产生什么样的输出即可,所以我们应该重点关注那些结论性的东西,数学证明之类的只是为了辅助理解,不应该成为我们的重点。

从上世纪二十年代到现在,量子力学已经诞生了百年时间,相信很多人都听说过“测不准原理”和“薛定谔的猫”,量子力学就是以这样违背我们正常认知的特性让人类爱恨交加。量子计算最早是由Paul Benioff在在上世纪80年代提出的,之后经过了Richard Feynman等人的发展和补充,量子计算开始真正进入人们的视野,目前为止,对量子计算的研究已经有了一定的成果,加拿大D-Wave系统已经研制成功16位量子比特的超导量子计算机,Google、微软等国际巨头也已经开始在量子计算方面发力,国内的阿里巴巴、中科院等也取得了一定的成绩,除了硬件上的成果,量子计算所需要的开发工具也已经逐渐成熟,Qbsolv、QCL、 LIQU等许多量子编程语言已经得到了实际的应用。

这份文档所专注的部分就是量子程序开发,并且使用微软公司推出的量子开发工具:Q#语言。Q#语言是由微软在2017年推出的新的量子计算开发工具,相比早先的量子开发语言,它引入了更多比较现代的编程元素,对开发者也更友好。

这份文档的内容主要分两大部分:

还有第三部分量子算法会逐步进行编写和完善,但目前不会出现在文档中。文档内容主要参考了一些书籍、论文、维基百科以及微软公司官方文档等,具体请见参考目录。

量子计算是一个广阔的领域,也是一门非常复杂的学科,它与物理、数学、计算机、信息学等学科有着较大的交联,因此学习它绝不是一件容易的事情,我也希望能与更多的人一起学习,一起进步。如果你在阅读文档的过程中发现有任何错误、不妥或疑惑,希望你能联系我,我的邮箱是:1036014410@qq.com

目录

写给开发者的量子计算入门教程--- 基于Q#语言描述前言目录第一部分 量子计算的基础知识第一章 单量子系统qubit1.2 单量子态的测量1.3 量子门1.4 Dirac符号1.5 量子态可视化第二章 多量子系统2.1 量子测量2.2 多量子门2.2.1 controlled-NOT门2.2.2 Toffoli门2.2.3 通用量子门集合第三章 量子线路3.1 量子线路的特点3.2 量子线路的矩阵表示3.2.1 受控量子门与矩阵3.2.2 量子线路与矩阵第二部分 微软Q#量子计算开发工具第四章 Q#语言概览4.1 安装Q#开发环境4.1.1 安装前的准备4.1.2 构建Q#语言开发环境4.1.3 验证刚刚安装的开发环境4.2 第一个Q#语言程序4.2.1 在Q#中创建一个贝尔态(Bell State)4.3 Q#程序结构4.3.1 语句4.3.2 注释4.3.3 命名空间4.4 创建量子叠加态4.5 创建量子纠缠第五章 数据类型和控制结构5.1 数据类型5.1.1 基本数据类型5.1.2 元组(Tuple)5.1.3 数组(Array)5.1.4 用户自定义类型5.1.5 用户自定义类型的兼容性5.2 变量5.2.1 变量的可变性5.2.2 代码块5.2.3 变量作用域5.3 表达式5.3.1 括号表达式5.3.2 代数表达式5.3.3 qubit表达式(Qubit expressions)5.3.4 泡利表达式(Pauli expressions)5.3.5 结果表达式(Result expressions)5.3.6 范围表达式(Range expressions)5.3.7 布尔表达式5.3.8 操作符优先级5.4 控制结构5.4.1 for循环5.4.2 Repeat-Until-Success循环5.4.3 if语句第六章 operation 和 function6.1 定义operation6.2 operation的变体:Adjoint和Controlled6.3 定义function6.4 return语句6.5 fail语句6.6 operation和function的类型6.7 递归6.8 部分调用第七章 与qubit共舞7.1 分配qubit7.2 基本量子门7.3 测量7.4 扩展操作7.5 borrowing第八章 Q#量子计算模拟器8.1 QuantumSimulator8.1.1 处理结果8.1.2 fail语句8.1.3 IDisposable8.1.4 Seed8.1.5 Thread8.2 QCTraceSimulator8.2.1 提供测量输出结果的概率8.2.2 使用QCTraceSimulator运行程序8.2.3 输入检查器8.2.4 非法量子比特使用检测器8.2.5 基本操作计数器(Primitive Operations Counter)8.2.6 深度计数器(Depth Counter)8.2.7 宽度计数器(Width Counter)参考资料to-do

第一部分 量子计算的基础知识

在经典计算中,信息存储和处理的基本单位是bit,每一个bit要么表示确定的0,要么表示确定的1。与之相似,量子计算中信息存储和处理的基本单位是qubit(量子比特,Quantum Bit),它也有两个状态 ,与经典bit不同的是,qubit并不处于一个特定的状态下,而是处于两个基本状态 的量子叠加态中,或者说它同时处于 状态。

正是这种诡异的特性,使得量子计算在展现着强大能力的同时,也因其太过违反人们的认知而常常让我们陷入困惑。量子计算与传统的计算机程序设计、开发之间的存在着显著的差别,在经典计算领域里,一切都是确定的,你可以精确地操纵每一个bit来为你服务,可是,一旦进入量子领域,主宰这个世界的就不再是你,每一个qubit都是一副既不可远观(观察造成量子态坍缩)也不可亵玩(量子纠缠)的高冷范儿。理解量子的行为模式、演进过程是学习量子编程的第一步,这一部分的内容主要是对量子、量子态、量子系统等量子计算基础内容的介绍,学习该内容需要对线性代数和概率论的相关概念有基本的了解,不过放心,对于其中的数学我会尽可能地使用简单、便于理解的语言进行描述,不会对学习带来困扰。通过本章的学习,希望能帮助你建立对量子系统的初步印象和认知。

第一章 单量子系统

qubit

qubit是量子计算中信息存储和处理的基本单位。对于开发者而言,我们不是物理学家,也不是数学家,我们不需要深究量子的内部结构和演变原理,一个qubit对我们而言就是一个抽象的数学模型,它的状态称为量子态,由一个含有两个元素的向量来描述,这个向量叫做量子态向量(也叫做态矢)。如下所示的向量都表示了某一个特定的量子态。

$\begin{bmatrix} 1 \ 0 \end{bmatrix}\begin{bmatrix} 0 \ 1 \end{bmatrix}\begin{bmatrix} \frac{1}{\sqrt{2}} \ \frac{1}{\sqrt{2}} \end{bmatrix}\begin{bmatrix} \frac{1}{\sqrt{2}} \ \frac{-1}{\sqrt{2}} \end{bmatrix}\begin{bmatrix} \frac{1}{\sqrt{2}} \ \frac{i}{\sqrt{2}} \end{bmatrix}$

图1.1 态矢量

当然,并不是任意的二维向量都能用来描述一个qubit的状态,描述量子态的向量的模长必须为1,所谓向量的模长定义如下:

$M(v) = \sqrt{|a{1}|^{2} + |a{2}|^{2} + ... + |a_{n}|^{2}}$

某些资料上使用术语范数,实际上范数有多种形式,为了避免混淆,此处使用了模长的概念。

 

其中, 是一个 维的矢量, 的元素。

图1中的几个向量中, 有着特殊的意义,它们代表着一个量子的基态,任意一个量子态都可以由它们的线性叠加来表示。实际上,一个量子的所有状态构成了一个量子态空间 是这个空间的一组基底,它们也对应着经典计算中一个bit值的0和1,在量子计算中,我们更常使用符号 来表示这一组基态矢量,这种符号叫做Dirac符号,我们会在后文中详细介绍。当然了,学过线性代数的同学肯定知道一个空间的基底不止一组,对于量子态空间而言同样如此。

1.2 单量子态的测量

在经典世界中,我们能够清楚地知晓一个对象的状态,例如一张桌子的大小,一栋楼的高矮,一个电平的高低等,但在量子系统中,一切都显得扑朔迷离。正如海森堡不确定性原理所言,“我们不可能同时知道一个粒子的位置和它的速度”。也就是说我们无法得到一个量子的确切状态。

!薛定谔的猫

图1.2 薛定谔的猫

相信很多人都听说过“薛定谔的猫”,当我们未对其进行观察时,猫处于既死又活的叠加态,一旦我们进行了观察,猫就立即处于要么死要么活的确定态。量子也是这样,根据量子力学原理,当对量子进行测量或观察(位置、动量等)时,必然会导致量子态坍缩。我们只能得到测量后的一种确定的状态,而无从知晓测量前量子的真正状态。

测量会导致量子态坍缩为某个基态( )。一个状态为 的量子,其状态可以表示为 ,其中 均为复数,对其进行测量后得到状态 的概率为 ,得到状态 的概率为 ,根据概率论的知识,我们可以得出 满足

上面的描述似乎说明了我们永远也无法得到一个量子的确切状态,无法得到确切状态的系统怎么会有用呢?并且又怎么为我们所用呢?

不要着急,我们慢慢揭开她神秘的面纱。

1.3 量子门

在经典计算中,对bit的处理和操作是通过一系列各种各样的门(Gate)来实现的。常见的有与门、非门、或门和异或门等,我们日常所写的程序最终都由这些门来具体执行。量子计算也遵循这一套路,通过各种量子门对量子进行相应的操作,常用的量子门有门等。各种常见的门对应的符号如下图所示,其中左侧为bit门,右侧为量子门。

经典门与量子门

图1.3 经典门与量子门

在上面的量子门中,X门对一个量子态取反,从这一点上来说,X门与经典的非门非常相似,不同之处在于X门作用于一个状态为量子时,得到的结果为 ,也就是交换了两个基态的系数,并不是改变它们的符号。

实际上,量子门与经典计算中的门操作之间并没有清晰明确的对应关系,并且对于每个量子门,其输入的qubit数量与输出的qubit数量必须保持一致(上图中量子门左右两侧的一条横线就代表着一个qubit),这一点与经典计算大不相同,我们都知道,与门、或门等都是接受两个bit输入而产生一个bit输出,这一基本差别也说明了虽然我们可以借鉴经典计算中的某些概念来加强对量子计算的理解,但始终要记住这两个领域存在本质的不同,千万不能生搬硬套。

我们使用向量来表示一个qubit的状态,那么自然而然地,每一个量子门操作也对应着一个矩阵。常见的量子门与对应的矩阵如下图所示。

!量子门与对应的矩阵

图1.4 量子门与对应矩阵

实际上,我们熟知的大小为 的单位矩阵 也对应一个量子门操作,当然这个门对量子态不产生任何影响。 门构成了Pauli量子门集,但我们通常会忽略 门。在上图中的几种量子门中, 门作用于 时仍然得到 ,作用于 时得到 门(Hadamard门)作用于 时得到 ,作用于 时得到 ,细心的你一定注意到了, 门( 门)实际上是 门(Phase门)的平方根,因为 的平方根(之所以称其为 而非 主要是历史原因)。这几种量子门在量子计算中非常重要,并且在后面的内容以及实际运用中也会频繁出现。

除了上面提到的量子门,那么还有其它的量子门吗?我负责任地告诉你,不仅有,而且有无穷多个。之所以有无穷多个量子门,是因为量子态空间是连续的,不像经典计算中,bit空间是离散的只有0、1两个值,因此量子态之间的转换关系、映射关系也是无穷无尽的。

每一个量子门操作对对应着一个矩阵,科学家已经证明,这个矩阵所需要满足的唯一条件就是它必须是幺正矩阵,同时这句话反过来也是成立的:只要一个矩阵是幺正矩阵,那么它就能表示一个量子门操作。那么什么是幺正矩阵呢?

 

定义一、一个 维复方阵的共轭转置矩阵与其逆矩阵相等,那么这个矩阵就是幺正矩阵。

 

以上面的为例,我们使用 表示 的共轭转置矩阵,那么,计算可得 ,也就是说 ,这里的 表示 的逆矩阵,因此矩阵 就是一个幺正矩阵,或者也可以说矩阵 是幺正的。通过简单的验算,我们可以证明上面提到的X、Y、Z门等都是幺正的。

那么为什么量子门矩阵必须是幺正的呢?这是因为,根据量子力学的原理,量子态的演变必须是幺正的。式1是Schrodinger 方程,它描述了一个量子态在时域上的演进,式中的普朗克常量,H是一个特定的汉密尔顿算子(Hamiltonian),这里我们不必对其进行深究。对式1进行积分运算后我们最终可以得到 时刻的量子态之间的演进过程,如式3所示。可以证明式3中的 是幺正的,它对应的幺正矩阵用 表示【详细证明过程请《Quantum_Computation_and_Quantum_Information》第82到83页】,因此量子门必须是幺正的。从另一个更直观的角度来看,量子空间是一个连续空间,一个量子态可以演进为任意其它量子态,同时演进后的量子态依然能回归最初的状态,这意味着量子门操作必须是可逆的,在这个过程中不能有信息的丢失,幺正矩阵恰恰就代表着这样的可逆过程。例如有一个状态为 的量子,先对其应用 门,再应用 门,因为对量子应用量子门操作相当于态矢量左乘相应的矩阵,因此上述过程可表述为 ,经过了两次门操作后,量子又回到了最初的状态。

$ih \frac{d|\psi \rangle}{dt} = H|\psi \rangle$ (1)

$\int{|\psi{t_1} \rangle}^{|\psi{t_2} \rangle} \frac{ihd|\psi \rangle}{H|\psi \rangle} = \int{t{1}}^{t{2}}dt$ (2)

$|\psi{t_1}\rangle = exp[\frac {-iH(t{2} - t{1})} {h}] |\psi (t{2}) \rangle= U(t{1},t{2}) | \psi (t_{1}) \rangle$ (3)

幺正矩阵有着许多非常有用而又有趣的性质。例如它作用于一个向量时不会改变该向量的模长,构成它的列向量或行向量同时也是一组标准正交基等,这些性质我们不再赘述,以后用到时我们会详细说明,感兴趣的同学也可以查阅相关资料。

1.4 Dirac符号

前面的章节中,描述一个量子的状态我们使用了向量和Dirac符号相结合的方式,现在我们对Dirac符号进行详细的讲解。

Dirac符号是以其提出者狄拉克的名字命名的,它在1939年由狄拉克提出之后,随即便与希尔伯特空间一起构成了量子力学的基本分析工具。Dirac将希尔伯特空间一分为二,成为两个相互对偶的空间,用右矢表示量子态矢量,形式为 ,用左矢表示量子态对偶矢量,形式为 ,右矢是一个列向量,左矢是右矢的共轭,它是一个行向量。这一符号体系非常简洁明了,右矢即态矢,其符号箭头指向右侧,左矢就是其共轭矢量,符号箭头指向左侧,我们使用 分别表示量子态的两个基态 ,注意 并不是0向量( )。Dirac符号也叫做“"符号,其提出者狄拉克将”括号(bracket)“这个单词一分为二,左边为”",右边为“",因此左矢又叫做”",右矢又叫做“"。

我们将量子计算中一些常用的运算总结在了表1.1中,其中包含了使用Dirac符号进行内积、外积等运算的形式。

 

表1.1 常用运算

运算功能
复数 的共轭,如
右矢,也叫做
左矢(右矢的对偶矢量),也叫做
两个矢量 的内积
两个矢量 的外积
两个矢量 的张量积
矢量张量积的简写形式
矩阵 的共轭
矩阵 的转置
矩阵 的转置共轭,
两个矩阵 的张量积
的内积,等价于 的内积

我们重点讲解一下内积、外积与张量积运算,这几种运算广泛应用于量子计算、量子程序设计、量子门分解等各种场合中,知悉这些运算的内涵和方法是学习量子计算的基础,但是请放心,我会以非常简单的形式告诉你这些运算的内涵,绝对不会让你为繁杂的数学运算所困扰。

首先来看矢量的内积运算。矢量的内积也叫做点积,假设我们有两个矢量 ,这两个矢量拥有相同的元素个数 ,那么它们的内积定义为:

$|\alpha \rangle \cdot | \beta \rangle = \alpha{1}^*\beta{1} + \alpha{2}^*\beta{2} + \cdots + \alpha{N}^*\beta{N} = \sum{i=1}^{N}\alpha{i}^* \beta_{i} $

其中 分别是这两个矢量的元素。从上面的等式中可以看出,两个矢量的内积就是第一个矢量中各元素的共轭与第二个矢量对应元素乘积的加和,使用Dirac符号可以非常方便地将内积表示为: ,其左侧部分 正是矢量 的复数共轭,且与 相互对偶。

两个矢量 的外积定义为 ,因为 分别为列向量和行向量,因此矢量的外积是一个方阵,这个方阵的大小为

两个矩阵 的张量积表示为 ,它的定义如下所示:

$A \bigotimes B = \begin{bmatrix} A{11}B \quad A{12}B \quad \cdots \quad A{1n}B \ A{21}B \quad A{22}B \quad \cdots \quad A{2n}B \ \vdots \quad \qquad \vdots \quad \qquad \vdots \quad \qquad \vdots \ A{m1}B \quad A{m2}B \quad \cdots \quad A_{mn}B\end{bmatrix}$

其中,矩阵 的大小为 。矢量可以看做是只有一列元素的矩阵,因此矢量之间的张量积也遵循着上面的运算形式,两个矢量 的张量积为:

$| \alpha \rangle \bigotimes | \beta \rangle = \begin{bmatrix} \alpha{1} |\beta \rangle \ \alpha{2} |\beta \rangle \ \vdots \ \alpha_{N}|\beta \rangle \end{bmatrix}$

1.5 量子态可视化

目前为止,我们对量子态以及量子门这些基本对象的描述都是通过数学工具进行的,这一节里我们使用另一种更直观的方式对它们进行可视化的描述。

首先,一个量子态可以表示为 ,这个形式使我们自然而然地想到使用一个二维空间来描述一个量子态, 分别是这个二维空间的坐标轴,态矢量 为一条由原点出发指向坐标 的有向线段,图1.5所示为态矢量 门作用于 的示意图。

二维空间态矢量

图1.5 量子态在二维平面内的示意图

但是由于量子态空间是一个复数空间,使用上面的二维平面并不能完备地对这个空间进行描述。我们现在重新审视量子态 ,因为 是复数,根据欧拉公式和三维空间坐标变换等数学工具, 可以表示为:

$|\psi \rangle = e^{ir}(cos \frac{\theta }{2}|0 \rangle + e^{i\phi }sin \frac {\theta}{2} | 1 \rangle)$ [公式的推导可以参考https://en.wikipedia.org/wiki/3-sphere]

其中 均为实数, 在这里是一个全局的相位变换,通常我们不用理会它,所以得到 ,这个式子可以用一个单位球面进行展现,如图1.6所示。

Bloch Sphere

图1.6 Bloch Sphere

这个三维单位球面就是Bloch Sphere,它的上、下两个极点分别表示 。Bloch Sphere不仅能用来表示量子态,同时能用来演示量子门作用于量子时量子态的演进。假设我们有一个状态为 的量子,对其应用 门,演进过程可以用下面的Bloch Sphere展示出来,首先该态矢量绕 轴向下旋转 ,最后绕 轴旋转 得到结果

H门演示

图1.7 门应用于 的演进过程

门是最重要的量子门之一,它作用于 时得到 ,作用于 时得到 ,这两个结果都是 的叠加态,因此 的一个重要作用就是创造量子叠加。根据量子测量一节的知识,对这两个量子态测量得到 的概率都是 ,也就是说测量结果是完全随机的,我们在开发过程中经常会用一些随机数生成算法,但这些算法实质上都是“伪随机”的,而上面的量子测量是“真随机”的,物理原理为这种随机性提供了强力保证,这也正是 门的重要性所在,在很多量子算法中,都会有使用 门的场景。

在量子态空间中,有无穷多的量子门,就有无穷多对应的幺正矩阵,那么随之而来的一个问题就是,每次应用新的量子门时我们都要重新定义一个幺正矩阵吗?每个量子门操作实际上最终都要由相应的硬件来完成,那面对这无穷多的量子门,我相信世界上最伟大的工程师也会望而却步。幸运的是,人们已经证明,存在一个量子门集合,这个集合中只有几种常见的量子门,它们经过不同的组合可以构建出任意的量子门,这个集合也叫做通用量子门集合(后文会有更详细的论述)。并且,任意的幺正矩阵都能分解成如下形式(下面的这些数学运算不需要理解或记忆,只要知道一个幺正矩阵可以被分解即可):

$U = e^{i\alpha}R_z(\beta)R_y(\gamma )R_x(\delta )$

其中,

$R_z(\beta) = e^{-i\beta Z/2} = cos \frac{\beta}{2}I - isin{\frac {\beta}{2}}Z = \begin{vmatrix}e^{-i\beta/2} & 0 \ 0 & e^{i\beta/2} {2}\end{vmatrix}$

$R_y(\gamma) = e^{-i\gamma Y/2} = cos \frac{\gamma}{2}I - isin{\frac {\gamma}{2}}Y = \begin{vmatrix}cos \frac{\gamma}{2} & -sin \frac{\gamma}{2}\ sin \frac{\gamma}{2} & cos \frac{\gamma}{2}\end{vmatrix}$

$R_x(\delta) = e^{-i\delta X/2} = cos \frac{\delta}{2}I - isin{\frac {\delta}{2}}X = \begin{vmatrix}cos \frac{\delta}{2} & -i sin \frac{\delta}{2} \ -isin \frac{\delta}{2} & cos \frac{\delta}{2}\end{vmatrix}$

它们分别是由 门等经过旋转等变换得到的,所以任意的量子门都可以通过其它的门构建出来。Bloch Sphere虽然可以很好的展现量子和量子门操作,但它也有自己的局限,比如在面对接下来要讲的多量子系统时就无能为力了。

第二章 多量子系统

一个量子系统的状态用一个向量来表示,假设有一个由两个qubit组成的双量子系统,系统中每一个qubit都有两个基本状态 ,那么显而易见这个双量子系统的基本状态就有4种: ,简写为: 。事实上,这几种双量子系统的基本状态就是每一个量子基本状态的张量积,根据前面所讲的张量积的计算方法,这几种基态实际上就是:

$|00 \rangle = |0 \rangle \bigotimes |0 \rangle = \begin{vmatrix} 1 \ 0 \ 0 \ 0 \end{vmatrix}|01 \rangle = |0 \rangle \bigotimes |1 \rangle = \begin{vmatrix}0 \ 1 \ 0 \ 0 \end{vmatrix}$

$|10 \rangle = |1 \rangle \bigotimes |0 \rangle = \begin{vmatrix} 0 \ 0 \ 1 \ 0 \end{vmatrix}|11 \rangle = |1 \rangle \bigotimes |1 \rangle = \begin{vmatrix} 0 \ 0 \ 0 \ 1 \end{vmatrix}$

这四个向量构成了双量子状态空间的基底,这个空间中其它向量都能由它们的线性叠加表示出来。所以一个双量子系统的状态 可以表示为 ,并且 为测量这个系统得到相应输出结果的概率。 当qubit数量为 时,多量子系统的基态就是系统内每个量子基态的张量积, 量子系统拥有个基态,它们构成了多量子系统量子态空间的基底,下面所示的就是3量子系统的基态。

$|000 \rangle = \begin{vmatrix} 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \end{vmatrix}|001 \rangle = \begin{vmatrix} 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \end{vmatrix}|010 \rangle = \begin{vmatrix} 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \end{vmatrix}|011 \rangle = \begin{vmatrix} 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \end{vmatrix}$

$|100 \rangle = \begin{vmatrix} 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \end{vmatrix}|101 \rangle = \begin{vmatrix} 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \end{vmatrix}|110 \rangle = \begin{vmatrix} 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \end{vmatrix}|111 \rangle = \begin{vmatrix} 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \end{vmatrix}$

随着 的增长,多量子系统的基态数量呈指数级增加, 时,这个量子系统所包含的基态数量甚至超过了宇宙中所有粒子的数量,这将是一个多么广阔的量子态空间!可以说,量子计算的强大能力正是来源于此,我们一步步去发现它背后广阔的天地。

2.1 量子测量

在前面的单量子系统中,我们也曾经提到过量子测量的概念。回想一下,测量会导致量子态向其基态坍缩,一个状态为 的量子,经过测量后得到 的概率分别为 ,同样,测量一个状态为 的双量子系统,得到结果 的概率分别为

量子测量得到的是确定的经典世界的结果,这个结果可以看做是量子系统在经典世界上的投影。

量子系统的测量操作用符号表示,一个由 个量子组成系统,其基态有个,这也就意味着有个不同的测量操作符,这些测量操作构成了一个测量集合 ,索引 表示经过测量后所得到的结果。一个量子态为 的系统,在经过 测量操作测量后,产生结果 的概率为:

$p(m) = \langle \psi | M{m}^{\dagger}M{m} |\psi \rangle$

在测量后,系统所处的状态为:

$\frac {M{m} | \psi \rangle} {\sqrt {\langle | M{m}^\dagger M_{m} |\psi \rangle}}$

并且,测量操作符必须遵循以下约束:

$\sum{m} M{m}^\dagger M_{m} = I$

同时,所有可能的测量记过的概率和必须是1,因此:

$1 = \sum{m}p(m) = \sum{m}{\langle \psi | M{m}^\dagger M{m} |\psi \rangle}$

从数学角度来看,量子操作符是量子态系统没个基底与自身的外积。我们以单量子系统为例,构建单量子系统的两个测量操作符为: ,观察这两个操作符,我们可以发现 ,所以 ,因此这两个操作符符合上面的要求,假设一个单量子系统的状态为 ,分别应用这两个测量操作,我们得到:

$p(0) = \langle \psi | M{0}^\dagger M{0} |\psi \rangle = \langle \psi | M_{0} |\psi \rangle = | \alpha |^{2}$

$p(1) = \langle \psi | M{1}^\dagger M{1} | \psi \rangle = \langle \psi | M_{1} | \psi \rangle = | \beta |^{2}$

这就是我们在前面提到的测量一个单量子时得到 的概率分别为 的由来,同时在测量后,这个量子所处的状态就变成:

$\frac {M_{0} |\psi \rangle} {|\alpha |} = \frac{\alpha}{| \alpha |} |0 \rangle$

$\frac{M_{1} |\psi \rangle}{| \beta |} = \frac {\beta} {| \beta |} |1 \rangle$

实际上结果就是

在多量子系统中,我们也可以仅测量其中一部分量子的状态。在这里我们要引入一个非常重要的双量子系统:Bell State(也叫做EPR pair)【引用由来】,这个系统的量子态为:

$\frac {|00 \rangle + |11 \rangle} {\sqrt{2}}$

该系统有一个非常有趣的特性:测量第一个量子时,得到 的概率为 ,此时系统测量后的状态为 ,得到 的概率同样为,此时系统测量后的状态为 ,也就是说,测得第一个量子的状态后,剩下的量子其状态与第一个是一致的,呈现出非常强的关联性。人们已经证明,Bell State中的这种强关联性甚至强于经典系统中这样的性质。这种性质的存在进一步说明了量子系统用于进行量子计算以及其他领域的可能性。【《Quantum_Computation_and_Quantum_Information》第17页】

2.2 多量子门

多量子门指的是作用于多量子系统上的受控的量子门,所谓受控指的是多量子门作用于系统时,系统中一部分量子状态的变换能影响其它的量子态。多量子门一般分为两个部分:控制部分和目标部分。控制部分的状态决定了目标部分的状态。

2.2.1 controlled-NOT门

最基本的多量子门是 (受控非门)门,也叫 门,以双量子系统为例,图2.1所示为双量子系统的 门和其对应的量子门矩阵,图中, 门上方的直线代表了控制量子,下方的直线代表着目标量子,当控制量子的装填为 时, 门作用于目标量子,为 时,目标量子不受任何影响。

controlled-NOT

图2.1

双量子 门输入也输出量子态的对应关系为:

$| 00 \rangle \rightarrow |00 \rangle ; |01 \rangle \rightarrow |01 \rangle ; |10 \rangle \rightarrow |11 \rangle ; |11 \rangle \rightarrow |10 \rangle$

从另一个角度看, 门的作用相当于对控制量子和目标量子进行模二加运算(异或运算),也就是说,对于状态为 的双量子系统,经 门作用后,其状态为 ,并且模二加运算的结果放在了目标量子中,上图中间部分的图像就是对这一过程的描述。

2.2.2 Toffoli门

严格地说,门并不属于量子计算的范畴。在前面我们曾经说过,经典计算中的部分门操作不是幺正的,或者说不是可逆的,而量子门是可逆的并且可以用其他的量子门进行表示,那么在经典世界中是否存在一个门,这个门可以用来表示其它的门并且它还是可逆的?

答案就是门。门有三个输入和三个输出,其中两个输入bit是 (控制bit),剩下的一个是 (目标bit),当两个控制bit都为 时,目标bit会被取反,相反则目标bit保持本身状态不变。下面的图就是门的基本形式和其输入输出对照表。

Toffoli门

图2.2

可以用来实现任意经典计算中的逻辑门,比如下面这个用门实现的与非门(NAND Gate)。

NAND

图2.3 门实现与非门

门非常重要,之所以非常重要在于虽然它本身是经典计算中的产物,但同样能够用于量子计算中。如前文所述,量子门中输入与输出的qubit数量必须是一致的,门完美地契合这一点,同时,下面的矩阵是门的矩阵表示形式,可以证明这个矩阵是幺正的,从而门也能应用于量子计算中。

$\begin{bmatrix} 1\quad 0 \quad 0\quad 0\quad 0\quad 0\quad 0 \quad 0 \ 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \end{bmatrix}$

门说明了很重要的一点:量子计算和经典计算可以通过某种形式得到形式上的统一,从现实世界的角度来看,这一点也是完全合理的,毕竟所有的物理系统都可以用量子力学进行描述,同时,人们也已经证明量子计算机也符合图灵机的标准,尽管对人们来说它难以立即。这种统一的观点能够为量子计算的发展带来很大裨益。

2.2.3 通用量子门集合

假设有一个量子门集合,其中包含了几种量子门,如果其他的量子门都能有这个集合中的元素通过不同的组合而实现【包括满足一定误差的条件】,那么这个量子门集合就是通用量子门集合

目前,这样的通用量子门集合有两个,一个包含量子门: ,另一个则包含:

UniversalGateSet

第三章 量子线路

单独的量子门、量子系统并不能够完成真正的计算,只有将它们通过一定的规则和形式组合起来才能进行有用的工作,这样的组合就是量子线路,量子计算机等价于量子线路。

目前来看,我认为理解量子线路是进行量子计算和量子编程开发的敲门砖。量子编程与传统编程的区别的一部分也表现在这里。我们平时所做的开发工作,所接触到的编程语言、编程模型,无外乎就是分支、循环、递归等等程序设计元素的组合,但在量子计算中一切都不相同,有时候很简单的工作,比如计算 ,如果用量子计算机去实现也会大费周折,但是量子计算的魅力也正在于此,从不同的视角和观点重新审视这个世界,就会有不同的感受和收获。

量子线路中主要包括量子门、输入和输出qubit等,每种元素在量子线路中都有其特定的符号表示,我们将常用的符号总结在了下面的图中。

量子线路符号

3.1 量子线路的特点

现在我们来看一个量子线路的示例:

!teleportation

图3.1 量子线路图示例

这是一个有三个输入的量子线路,三个输入qubit分别为一个为止状态的 和两个,我们将 表示为 在图中,我们将线路中某一位置的量子态用虚线进行了标示。首先,两个 经过一个 门和一个 门的作用后,状态变为,此时与第一个量子 构成的三量子系统的状态为:

$| \varphi_{0} \rangle = |\varphi \rangle \otimes [\frac {1} {\sqrt{2}}(| 00 \rangle + | 11 \rangle)] = \frac{1}{\sqrt{2}}[\alpha |0 \rangle (| 00 \rangle + | 11 \rangle) + \beta |1 \rangle (| 00 \rangle + | 11 \rangle)]$

之后,再又一个 门和 门的作用下,我们得到这个三量子系统的状态为:

$| \varphi_{1} \rangle = \frac{1}{\sqrt{2}}[\alpha (|00 \rangle + |11 \rangle) + \beta|1 \rangle (|10 \rangle + |01 \rangle)]$

$|\varphi_{2}\rangle = \frac{1}{2}[\alpha(|0 \rangle + |1 \rangle)(|00 \rangle + |11 \rangle) + \beta(|0 \rangle - |1 \rangle)(|10 \rangle + |01 \rangle)]$

状态 又可以写做:

$| \varphi_{2} \rangle = \frac{1}{2}[|00 \rangle (\alpha |0 \rangle + \beta |1 \rangle) + |01 \rangle (\alpha |1 \rangle + \beta |0 \rangle) + |10 \rangle (\alpha |0 \rangle - \beta |1 \rangle) + |11 \rangle (\alpha |1 \rangle - \beta |0 \rangle)]$

然后,使用测量 分别对前两个量子进行测量,测量导致量子态坍缩输出的是经典的bit,这一点线路图上也有相应的显示。

那么从这个线路图上我们能够得到什么呢?

  1. 量子线路图的输入和输出量子数量必须是一致的。这一点我们在将量子门时已经说过,既然量子线路是由量子门组成的,那么量子线路必然也要遵循量子门输入输出量子数一致这一原则。
  2. 量子线路中不能存在环状结构,也就是说在其中不能有循环。
  3. 在电路图中,我们可以将几根电线合并在一起或者从一根电线中分出多跟电线,但在量子线路中,这是不允许也是行不通的。【前面应当补充no-coloning原理】。

这就是量子线路的特点,从中可以看出,它与我们日常所见到的电子线路非常不同。

我们继续讨论上面的量子线路图。在经过了两个测量符测量后,我们对第三个量子进行进一步的转换操作。每一个测量操作会得到两个结果: 或者 ,因此我们就会得到4中测量结果,我们将每种结果以及对应的转换后的结果写在下面的表中:

表xxx 测量结果【矩阵从右至左产生作用,因此写在右边】

转换操作结果
00
01
10
11

现在我们知道了这个线路的输出结果,也知道了这个线路的工作过程,但是!这个线路到底有什么作用呢?从这些结果中也看不出个所以然来。那么我告诉你,我们刚刚进行了一次量子通信,你相信吗?

我们现在来设想一下,假设有两个人A和B,我们首先使用 门和 门作用于两个量子态 ,这时我们会得到他们的叠加态,也就是图中的 ,然后将处于叠加态的两个量子分别交由A和B,随后A和B两个分道扬镳,许多年后,A想起了当年青梅竹马的B,他想送给B一个量子表达自己的思念之情(不要问我为什么要送量子),但是B并不知道A送给他的量子到底是什么状态,并且如果B要测量这个量子,那这个量子立马就会发生坍缩,从而导致B永远也不能知道A到底传送给了他什么,这时候他们当年分离之时各自拥有的那个量子就起作用了,A测量自己拥有的量子和传送的量子,得到一个结果,这个结果由两个bit构成(我们上面的测量结果),分别是: ,A同时将这个测量结果传送给B,根据这个结果和上面的表格,B就能够知道A到底传送给了他什么。这就是量子隐形传态,实际上A并不需要真的给B传送一个量子,他只需要完成测量并将测量结果传送给B就可以了。

量子隐形传态描述的是一种量子之间存在的一种超距作用,处于纠缠态的量子,无论它们相隔多远,只要之中一个量子的状态发生了变换,另一个会立即产生相应的变化。但是要特别指出的是,量子隐形传态并不能超光速,以上面的通信过程为例,A在传给B量子的同时还要传输经典bit信息,而这一过程是不能超光速的,如果没有这些进店bit信息,B也就无从知晓传输的量子态,也就无法完成通信过程。因此量子隐形传态并不能超光速。

同时,上面的结果似乎能够让我们克隆一个量子 ,这么一来就违背了不可克隆原理,但实际上最初的量子 在测量后发生了量子态坍缩,我们得到的结果是对其他量子进行一定的量子门操作的结果,因此量子并没有被克隆。

3.2 量子线路的矩阵表示

量子门可以用矩阵进行表示,同样,由量子门构成的量子线路也能表示为一个矩阵。本节内容就是讲量子线路与矩阵之间的转换关系。

3.2.1 受控量子门与矩阵

受控量子门的矩阵表示很简单,以双量子系统来说,受控量子门对应矩阵是一个 大小的矩阵,矩阵的左上角是一个 的单位矩阵,左下角就是相应的被控量子门矩阵了,其他部分都为0。例如我们学过的受控量子门 ,它的矩阵表示形式就是:

$\begin{bmatrix} 1 \quad 0 \quad 0 \quad 0 \ 0 \quad 1 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 1 \ 0 \quad 0 \quad 1 \quad 0 \end{bmatrix}$

根据这个规则,我们再来构建其他受控门的矩阵形式,例如受控 门,它的符号表示和矩阵形式如下图所示:

ControlledZ

图3.2

在上面的 门中,控制qubit在位于上方,目标qubit在下方,如果我们将它们的位置交换一下,那么相应的矩阵会改变吗?答案是不会,我们在下面的图中分别画出了两种线路图和它们的输入输出之间的关系,结果表明,上下位置的颠倒并不会对量子线路的结果造成影响,因此它们的矩阵形式是一样的。

ControlledZReverse

图3.3 翻转

3.2.2 量子线路与矩阵

我们以下面的量子线路为例讨论量子线路与矩阵之间的转换方法。

QuantumCircuit

图3.4 量子线路示例

对于这样一个量子线路,我们首先将其以门为单位进行纵向的切分,就像图3.5中所示那样,每个分区内部的量子门矩阵的张量积就是这个分区的矩阵表示。

QuantumCircuitDIvide

图3.5 量子线路分区

得到每个分区的矩阵后,整个线路的矩阵表示就是所有分区矩阵按从左到右的顺序相乘所得的矩阵乘积,那么上面的量子线路最终的矩阵表示为:

虽然上面是双量子系统的线路图,但无论多少个量子,线路图矩阵的构造方法是一样的。

但是有时候我们也会遇到一些麻烦,比如下面这样的多量子门线路,它门对应的矩阵是什么呢?

TrivialGates

图3.6 量子线路

这样的线路麻烦之处在于,控制量子和目标量子之间跨过了另外的量子,直接用上面分区中的方法是不行的,为此我们要寻找其它方法。

一个量子线路对应一个矩阵,我们将这个矩阵设为 ,矩阵作用输入的量子态向量得到相应的输出,这个过程可以描述为:

以图xxx中右侧的量子门线路为例,我们来求取矩阵 。这个量子门的作用是交换第 和第 个量子态而第二个量子态不受影响,我们将这个三量子系统的输入输出写在表3.1中:

表3.1 输入输出表

输入输出

将上表的量子态用矩阵运算的形式可以表示为:

$A \begin{bmatrix} 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \end{bmatrix} = \begin{bmatrix} 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \ 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 1 \quad 0 \quad 0 \quad 0 \quad 0 \ 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 0 \quad 1 \end{bmatrix}$

上面等式左侧的矩阵中,每一列代表一个输入的量子态向量,右侧矩阵中每一列代表一个输出的量子态向量。那么答案很明显了,矩阵 就是上面等式中右边的部分,因为实际上右乘了一个单位矩阵。

因此面对上面两个比较麻烦的量子门线路,这样的方法也能很快求出相应的矩阵。实际上,这个方法是通用的,任何的量子线路矩阵都能使用这种方法求出来,只要正确地写出了输入和输出的量子态向量。

第二部分 微软Q#量子计算开发工具

第四章 Q#语言概览

Q#是一门量子编程语言之一,它最大的特点就是它是微软的儿子(^_^)。

目前看来,相比量子计算的硬件发展水平,与量子计算有关的软件设施发展的要好很多,Qbsolv、QCL、 LIQU等许多量子编程语言已经得到了实际的应用,Q#就是其中之一。

Q#是一门非常年轻的语言,它由微软在2017年推出。Q#从Python、C#、F#等语言中汲取了许多元素,这使得相比于它的前辈们,它看起来更现代化,更时髦,同时对开发者也更友好,使用也更方便。Q#语言是一门抽象程度很高的语言,它对量子计算机的相关逻辑进行了高度封装,可以方便地在不同架构的量子计算机上进行移植,这也极大地减轻了开发人员的负担。

从宏观上来看,Q#语言遵循了量子计算机与传统计算机的分立模型,即量子计算机仅负责量子计算相关部分,其它任务完全由经典计算机来完成,两者之间通过一定的方式进行通讯。一个Q#程序分为两个部分,使用C#编写的驱动部分,这一部分即是对经典计算机的抽象,另一部分就是用Q#语言编写的量子计算程序,这种分立模型简化了程序的开发和维护,并且也符合现实需要。所以在Q#程序中实际上分了三个层次:

  1. 经典计算。完成数据的输入/输出,设置和触发量子计算机并处理计算结果等经典计算机能够处理的任务。
  2. 量子计算。直接在量子计算设备上应用量子算法完成计算任务。
  3. 量子计算在处理过程中调用经典计算。

接下来,就让我们走进这个年轻而又魔幻的Q#世界。

4.1 安装Q#开发环境

4.1.1 安装前的准备

我们以Windows系统为例,如果没有安装Visual Studio 2017,可以通过以下步骤安装免费的 Community版Visual Studio 2017:

  1. 访问Visual Studio下载页面 ,并选择Community版本进行下载(如果有钱也可以下载另外的版本)
  2. 下载完毕后双击安装文件
  3. 注意:安装开始前程序会让你选择特定的开发环境和工具,记得一定要选上 Universal Windows Platform development .NET desktop development
  4. 在选择好需要的环境和工具后点击“安装”即可。

4.1.2 构建Q#语言开发环境

使用Q#语言需要安装Q#语言的开发工具包,安装过程也是很简单的。

  1. 访问微软Q#语言主页,点击页面上的 "Download" 按钮,此时页面将跳转至Q#开发工具包的下载界面
  2. 在下载界面的右侧,填写一些必要的信息,包括你的名字、联系方式等等,之后点击右下角的 "Download Now" 按钮,就会开始下载开发工具包。工具包是一个vsix格式的文件,大小只有1M左右。
  3. 双击下载好的文件,稍等片刻Q#语言的开发工具就安装在了Visual Studio2017中。

4.1.3 验证刚刚安装的开发环境

我们使用微软提供的一些例子和库对刚刚安装的环境进行检测,以验证我们是否正确安装好了Q#语言的开发环境。

  1. 克隆GitHub上微软官方提供的Q#语言例程

  2. 使用Visual Studio打开 QsharpLibraries.sln 1. 如果此时弹出 "Install Missing Features" 的面板,点击“安装”

  3. 选择 Teleport 示例程序并运行,如果出现了类似下面这样的输出,那么说明我们的Q#语言开发环境安装正确,你可以开始在量子世界里的挑战了!

如果出现了与NuGet有关的错误,使用NuGet package restore中的方法重置安装包即可.

4.2 第一个Q#语言程序

这一节的主要内容包括:

4.2.1 在Q#中创建一个贝尔态(Bell State)

现在,我们已经安装好了Q#语言的开发环境,开始编写我们的第一个量子计算程序,在这个程序中,我们以一个初始态为|0>的量子比特为起点,对其进行一些操作,向你展示Q#程序的基本面貌。

(1) 创建工程和解决方案

打开Visual Studio 2017,依次点击“文件-->新建-->项目”,在出现的新建项目对话框中,选中左侧栏目中的“Visual C#”,此时在对话框中部会出现许多条目,找到“Q# Application”并选中,设置项目名称为Bell,如下图所示。如果没有找到“Q# Application”的条目,检查对话框上部列表选择框中是否选中了“.NET Framework 4.6.1”。

CreateASharpApp

(2) 编辑Q#代码

在完成工程创建后,此时会有两个文件:Driver.csOperation.qs,前者就是使用C#编写的驱动程序,后者才是真正的Q#代码文件。

首先将Operation.qs文件重命名为Bell.qs,Visual Studio在创建工程时会自动生成一部分代码,此时Bell.qs中的内容类似于这样:

然后将代码中第二个“Operation”改为Set,并且将其后面第一个括号中的内容改为desired: Result, q1:Qubit,此时Bell.qs中的内容应为:

现在,将以下代码键入body后的大括号中:

Bell.qs中的代码为:

现在我们来解释一下上面的代码。上面的代码中定义了一个Q#语言中的 operationoperation是Q#语言中一个基本的执行单元,它相当于其他编程语言如C/C++、C#和Java中的函数。一个operation的参数是一个元组,在operation名字之后的括号中指定各个参数,参数之间用逗号分隔,参数的定义形式为:“arg-name : arg-type",operation的返回值与其参数形式类似,在参数列表之后的括号中指定,两个括号之间由一个冒号分隔。Q#语言中,operation可以指定多个返回值,也可以将括号留空表示没有返回结果。

一个operation中包含一个body段,在body段中的代码就是该operation功能的实现代码。所以上面代码所完成的事就是,定义了一个名为Set的operation,它接受两个名称分别为desiredq1的参数,没有返回值。这个operation首先测量q1的量子态(M),如果结果与desired的量子态相同,则返回,否则就对q1应用门,使其状态翻转。

现在来编写一个测试程序调用这个operation并观察结果。在Bell.qs中添加如下代码,可以看到这也是一个operation,将其添加在Set之后。

上面的operation BellTest中定义了一个循环count次的控制结构,在每次循环中,首先将一个量子比特设置为initial,然后测量这个量子的状态,如果为1,将变量numOnes加一,所有循环结束后,我们将得到在这个过程中共有多少次测量的量子比特状态为,多少次为。在最后,我们把量子比特重新设置为了,使其处于一个特定的状态。

从上面的代码中可以看出,Q#语言使用了与C#相似的分号、括号等来标示程序的结构,并且Q#也拥有与C#类似的if控制语句。

默认情况下,Q#中的变量一旦被绑定,其值就不能再改变,关键字let用来将值绑定在一个变量上,同时Q#中operation的参数也是不可变的。

如果需要使用可变的变量,就需要使用mutable关键字对变量进行声明,就像上面的程序中那样,经过mutable关键字声明的变量,其可以使用也只能使用set语句重新赋值。

Q#中定义变量不需要明确指明数据类型,变量的类型由编译器根据其具体值进行自动推断。

using语句在Q#中有着特定的用途,它用于为程序分配qubit。在Q#中,所有的qubit都是动态分配和释放的,并且每个qubit的初始状态都为using语句在代码段的起始处分配qubit,并在代码段结束后自动释放它们。

Q#中的for循环在一个范围之内进行迭代,范围在Q#中是一种特殊的数据类型,一个范围可以直接由起始和终止的值来指定,两个值之间使用..连接。例如1..10就表示1,2,3,4,5,6,7,8,9,10,默认步长为1,如果需要不同的步长值,则直接指定即可,其语法为1..2..10,这个范围表示的数值就是1,3,5,7,9。值得注意的是,Q#中的范围是一个闭区间,也就是包含起始值和终止值。

Q#中使用元组来传递多个变量,上面的operation BellTest的返回值就是由两个Int数据(Int, Int)组成的元组。

(3) C#驱动代码

现在我们将目光转向Driver.cs文件中,在创建项目时,Visual Studio会自动生成一部分C#代码,内容如下:

Main方法中,我们输入以下代码:

上面的C#代码包含4个部分: