附录1 量子计算数学基础
附录2 量子编程工具的安装与使用
附录3 量子化学工具的安装与使用
对于不具有任何高等数学基础背景的读者,本节将从集合与映射、向量空间、矩阵与矩阵的运算、矩阵的特征、矩阵的函数以及线性算子与矩阵表示等相对简单易懂的数学开始讲起,以便各位循序渐进理解量子计算的数学原理。
当提到中国古代四大发明时,大家一般会想到造纸术、印刷术、指南针和火药;当提到中国的四大名著时,大家会想起吴承恩的《西游记》、罗贯中的《三国演义》、曹雪芹的《红楼梦》、施耐庵的《水浒传》。生活中有很多类似于四大发明、四大名著的称呼,比如:世界上的所有国家、彩虹的颜色、三原色等等,这些称呼都有一个共同的特点,就是将具有明确地相同特性的事物放在一起的统称。
在数学上,把具有某种特征事物的总体称为集合(set),组成该集合的事物称为该集合的元素(element)^[56,57]^。比如,中国的四大名著,就可以称为一个集合,《西游记》则是其中的一个元素。有时为了方便与简洁,在数学上会引进一些符号来表示一些数学名称,这就使得数学上了一个台阶,当通过练习知道这些符号代表的内在含义时,就很方便地去推导以及交流。
但是这种符号表示的简化也为后来学习者或多或少带来了一些障碍。因为当没有介绍过某个概念,突然看到一个符号表示,智力再好也不可能知道它代表的含义。比如世界上第一个人发明“※”表示太阳,但他没有告诉你,这符号“※”表示太阳,而是给你画出这个符号“※”,问你这是什么时,这个问题换着任何人都不可能回答,除非是发明者,因为只有他一个人知道这个符号代表的含义,当人们都开始用这个符号“※”表示太阳时,这就极大地方便人们之间的交流,因为这符号写起来相对简单些。如果过仅有部分人知道,还可以作为密码来使用,从某种意义上来说,数学也是一门符号化的语言。所以,在学习数学的时候,首先要弄明白符号背后的含义是什么。
下面,引进大写的拉丁字母A、B、C等符号来表示集合,用小写的拉丁字母a、b、c等符号表示集合的元素,需要注意的是有的时候拉丁字母不够多或者不方便时,也会引进其他的符号表示元素。比如用B这个符号表示四大名著,用$$b_1$$表示《西游记》、$$b_2$$表示《三国演义》、$$b_3$$表示《红楼梦》、$$b_4$$表示《水浒传》。$$b_1$$是B的元素,在数学上,通常说$$b_1$$属于B,记做$$ {b_1} \in B$$。假设,h表示《海底两万里》,h就不是集合B的元素,就说h不属于B,记做$$ {h} \notin B$$。
像四大名著这样的集合有有限个元素,称为有限集;也可以通过列举法来表示这个集合。例如,可以将C的元素一一列举出来写在大括号里面
如果遇到像自然数集(自然数组成的集合)有无限多个元素该如何来表示呢?通常称有无限多个元素的集合为无限集,好在自然数集有了0和1,其他的数就都知道了;也可以通过列举法来列举出有限个,其余的用省略号代替。自然数集$$N$$用列举法表示为
同样地,用列举法可以表示正整数集(所有正整数组成的集合)
整数集(所有整数组成的集合)
那像有理数集(所有有理数组成的集合)就不能用列举法来表示了,因为任意两个有理数之间一定还存在有理数(比如这两个有理数之间的中间值)。将有理数的性质描述出来写在大括号中:
这种将用元素具有的性质来表示的方法叫描述法。若集合$$A$$由具有某种性质$$\Gamma$$ 的元素$$a$$组成,则描述法的一般形式为
同样地,可以用描述法来表示无理数集
其中符号$$\forall$$表示任意的。
同时,也可以用自然语言描述法来描述集合,比如,实数集R是所有有理数和无理数组成的集合。
在量子计算中常常会用到复数集
其中$$ {c}= {a}+ {bi}$$表示复数(complex number),实部$$a$$和虚部$$b$$都是实数,$$i$$ 在这里表示一个符号并且满足$$i^{2}=-1$$ 。 有时,用有序数对$$(a,b)$$来表示复数$$ {a}+b {i}$$。
两个复数$$c_{1}=a_{1}+b_{1} i$$ 和$$c_{2}=a_{2}+b_{2} i$$ 相等的充要条件是实部和虚部分别对应相等,即 $$ c_{1}=c_{2} \Leftrightarrow a_{1}=a_{2}, b_{1}=b_{2} $$
两个复数$$c_{1}=a_{1}+b_{1} i$$和$$c_{2}=a_{2}+b_{2} i$$ 做和相当于实部和虚部分别对应做和,即
两个复数$$c_{1}=a_{1}+b_{1} i$$和$$c_{2}=a_{2}+b_{2} i$$ 做差相当于实部和虚部分别对应做差,即
两个复数$$c_{1}=a_{1}+b_{1} i$$和$$c_{2}=a_{2}+b_{2} i$$ 乘法被定义为
因为$$i^{2}=-1$$,因此
比如
在给出两个复数除法的定义之前,先定义复数$$c=a+b i$$的复共轭(complex conjugate)为
或
由复数的乘法,可知
那么根据复共轭的定义,两个复数)$$c_{1}=a_{1}+b_{1} i$$ 和$$c_{2}=a_{2}+b_{2} i$$ 除法被定义为
$$ \frac{c_{1}}{c_{2}}=\frac{c_{1} \bar{c}{2}}{c{2} \bar{c}{2}}=\frac{\left(a{1}+b_{1} i\right)\left(a_{2}-b_{2} i\right)}{\left(a_{2}+b_{2} i\right)\left(a_{2}-b_{2} i\right)}=\frac{\left(a_{1} a_{2}+b_{1} b_{2}\right)+\left(b_{1} a_{2}-a_{1} b_{2}\right) i}{a_{2}^{2}+b_{2}^{2}} $$
比如,将$$\frac{1+2 i}{3-4 i}$$写成$$a+b i$$的形式为
把集合看成一个对象,那么集合之间有什么关系呢?集合是由元素组成,因此还要从元素进行分析。
假设有两个集合$$S_{1}$$和$$S_{2}$$,如果集合$$S_{1}$$的元素都是集合$$S_{2}$$的元素,那么称$$S_{1}$$是$$S_{2}$$的子集,记作$$S_{1} \subseteq S_{2}$$(读作$$S_{1}$$包含于$$S_{2}$$ )或$$S_{2} \supseteq S_{1}$$(读作$$S_{2}$$包含$$S_{1}$$)。比如无理数集就是实数集的子集,因为无理数集中的每一个元素都在实数集中。
如果两个集合中的元素都相同,那么称这两个集合相等,即是说,如果集合$$S_{1}$$与集合$$S_{2}$$互为子集,那么就称集合$$S_{1}$$与$$S_{2}$$相等,记作$$S_{1}=S_{2}$$ 。例如,偶数集$$S_{1}$$ 与集合$$S_{2}={n \mid n % m=0, m \in Z, n \in Z, m=2}$$ 相等。注:这里的%表示取余运算(取余运算是指n除以m得到的余数)。
如果$$S_{1} \subseteq S_{2}$$且$$S_{1} \neq S_{2}$$,那么称$$S_{1}$$是$$S_{2}$$的真子集,记作$$S_{1} \subset S_{2}$$(读作$$S_{1}$$真包含于$$S_{2}$$)或$$S_{1} \subset S_{2}$$(读作$$S_{2}$$ 真包含$$S_{1}$$)。比如,$$Q \subset R$$。
通常,没有元素的集合称为空集,记作$$\varnothing$$。比如,由既是有理数又是无理数的实数为元素组成的集合。
类似于数的运算,集合也有运算规则。由于集合是具有共同特征事物的全体,因此会用到将两个集合
比如,有理集
由所有属于
比如,有理集
由所有属于
比如,有理集
差集的一种特殊情况:当
比如,在复数集
除了集合之间的交、并和差运算之外, 还有一种常用的生成新集合的方式-直积或笛卡尔 (Descartes) 积。设
比如,
类似于数的运算法则,集合也有自己的运算法则。集合的交、并和补运算满足以下法则。
假设有任意的三个集合
(1) 交换律
(2) 结合律
(3) 分配律
(4) 对偶律
若对这些规则的证明感兴趣, 可以通过集合相等的定义来证明。
上面讲述了集合, 然而有的集合之间并不是完全孤立的,而是有对应关系的。比如,中国四大名著的作者组成的集合A与四大名著B之间存在对应关系。
将这种普遍的共性抽象出来,设$$D$$、$$E$$ 是两个非空集合,如果存在一个对应法则
其中y称为元素
而元素$$x$$称为元素$$y$$在映射$$f$$下的一个原像; 集合$$D$$为映射的定义域;集合E称为映射的陪域;
其中符号$$\exists$$表示存在,可以看出,f的值域是f的陪域的子集。
集合D到自身一个映射,通常称为D上的一个变换。集合D到数集E的一个映射,常称为从D到E的函数。
如果映射f与映射g的定义域、陪域、对应法则分别对应相同,那么称这两个映射相等。
映射$$f: D \rightarrow D$$如果把D中每一个元素对应到它自身,即 $$ \forall x \in D,有f(x)=x, $$ 那么称f为恒等映射(或D上的恒等变换),记作$$I_{D}$$。
先后施行映射$$g: \mathrm{S}{1} \rightarrow S{2}$$和$$f: S_{2} \rightarrow S_{3}$$,得到$$S_{1}$$到$$S_{3}$$的一个映射,称为$$f$$与$$g$$的合成(或乘积),记作$$fg$$。即
定理 映射的乘法适合结合律。即如果
$$ h: S_{1} \rightarrow S_{2}, g: \mathrm{S}{2} \rightarrow S{3}, f: S_{3} \rightarrow S_{4} $$
那么
学习量子计算,要对量子力学有所了解,而量子力学是由希尔伯特空间来描述的,希尔伯特空间又是向量空间,因此首先要来介绍向量空间(vector spaces)^[59-62]^。
向量空间本质上是一个由向量组成的集合,然后引进一些运算规则。那什么是向量呢?向量相对数量来说的,数量是只有大小的量,而向量不仅有大小而且还有方向的量。有时,向量也称为矢量。可以认为向量是数量的一个自然的扩充。
假设有一个数域
其中
在数学上,向量常用加粗的小写拉丁字母
两个向量
其中
规定数量
设
(1)
(2) $$ (|u\rangle+|v\rangle)+|w\rangle=|u\rangle+(|v\rangle+|w\rangle)$$; (加法结合律)
(3)
具有该性质的元素
(4) 对于
$$
|v\rangle+|\bar{v}\rangle=|\hat{0}\rangle
$$
具有该性质的元素
(5)
(6)
(7)
(8)
数域$K$上所有n元有序数组组成的集合
在$V$上,定义加法运算和数乘运算之后,满足的8条运算法则可以推导出向量空间的一些其他性质:
性质1:
性质2:
性质3:
性质4:
性质5: 如果
性质6:
若
(1)
(2)
则称
若想研究数域$K$上向量空间$V$的结构特征,根据向量空间的定义,只能从$V$的向量的加法以及数乘这两种运算开始。对于$V$中的一组向量
根据$V$中加法和数乘的封闭性,
为了方便,像
如果
那么称
若向量组
若向量空间V中的任意向量都可以由向量组
线性无关的生成集称为极小生成集。向量空间的极小生成集定义为向量空间的基,极小生成集中向量的个数定义为向量空间的维数。比如,当数域
组成的集合。
因为向量空间
都可以写成向量组
并且向量组
因此,当知道向量空间的基时,就可以来用它们来线性表示该向量空间中的任意的向量。也可以说这组基张成了这个向量空间。
然而需要注意的是向量空间的基并不唯一。比如,一组向量
就可以作为向量空间
可以写成
假设给定
将
从二维复向量空间的例子可以看出,同一个向量在不同的基下有着不同的坐标表示。
定理 在
证明 设
假设
$$ |u\rangle=\bar{\alpha}{1}\left|b{1}\right\rangle+\bar{\alpha}{2}\left|b{2}\right\rangle+\cdots+\bar{\alpha}{n}\left|b{n}\right\rangle $$
将这两个不同的坐标表示的向量做差,得到
$$ |u\rangle-|u\rangle=\left(\alpha_{1}-\bar{\alpha}{1}\right)\left|b{1}\right\rangle+\left(\alpha_{2}-\bar{\alpha}{2}\right)\left|b{2}\right\rangle+\cdots+\left(\alpha_{n}-\bar{\alpha}{n}\right)\left|b{n}\right\rangle $$
即
$$ |\hat{0}\rangle=\left(\alpha_{1}-\bar{\alpha}{1}\right)\left|b{1}\right\rangle+\left(\alpha_{2}-\bar{\alpha}{2}\right)\left|b{2}\right\rangle+\cdots+\left(\alpha_{n}-\bar{\alpha}{n}\right)\left|b{n}\right\rangle $$
因为基是线性无关的,所以有
即
从而假设不成立,因此在给定基下,任意给定向量的坐标表示唯一。
向量的内积是一个从向量空间
(1)映射
(2)交换共轭性,即
(3)自内积非负性,即
等号成立当且仅当
其中
在量子力学中,内积
这里
$$ \langle u|:=\left[u_{1}^{}, u_{2}^{}, \cdots, u_{n}^{*}\right] $$
将拥有内积的空间称为内积空间(inner product space)。量子力学中常提到希尔伯特空间(Hilbert space),在有限维的情况下,希尔伯特空间与内积空间是一致的。无限维的情况,这里不加考虑。
若向量
向量
若向量
设
则称这组向量为向量空间的标准正交 (orthonormal) 基。
标准正交基能带来很多方便,比如在计算向量的内积时,就可以将向量的坐标对应相乘。
假设知道向量空间的一个非标准正交基
接着对
在量子计算中,通常,用一组带有指标
事实上,当提到矩阵时,并不陌生,只是在平时不把它叫做矩阵而已,而通常称作表。比如,一个班级有35名同学,这学期要修6门课程,在本学期期末考试后,为了便于管理和分析,老师将每位同学的各科成绩放在一起,做成一个35行,6列的表。在日常生活或在其他学科中有很多类似的表,将它们的特点进行提取,进而形成数学上的矩阵这样的抽象概念。其实这样的抽象过程也并不陌生,比如自然数的发明就是这样的一个过程。
定义1.4.1 由
矩阵通常用大写英文字母
如果某个矩阵
元素全为 0 的矩阵称为零矩阵,简记作
数域
定义 1.4.2 设矩阵 $$A=\left(a_{i j}\right){m \times n}$$, 若矩阵 $$B=\left(b{i j}\right){n \times m}$$ 满足 $$a{i j}=b_{j i}$$ ,则称矩阵
定义 1.4 .3 设 $$ {n}$$ 级矩阵 $$A=\left(a_{i j}\right){n \times n}$$ ,称 $$T=\sum{i=1}^{n} a_{i i}$$ 为矩阵
(1)$ \begin{aligned} {tr}(A B)=\text{tr}(B A)\end{aligned} $
(2)$ \begin{aligned} {tr}(A+B)=\text{tr}(A)+{tr}(B )\end{aligned} $
由矩阵
定义 1.4 .4 把一个矩阵
例如矩阵
从而
定义1.4.5 设
则称矩阵
定义1.4.6 设
则称矩阵
(2)$(A+B)+C=A+(B+C)$
(3)$A+0=0+A=A$
(4)$A+(-A)=(-A)+A=0$
(5)$1 A=A$
(6)$(k l) A=k(l A)$
(7)$(k+l) A=k A+l A$
(8)$k(A+B)=k A+k B$
利用负矩阵的概念,可以定义矩阵的减法如下:设
定义1.4.7 设 $$A=\left(a_{i j}\right){m \times n}, B=\left(b{i j}\right)_{n \times s}$$, 令
其中
矩阵乘法需要注意以下两点:
(1) 只有左矩阵的列数与右矩阵的行数相同的两个矩阵才能相乘;
(2) 乘积矩阵的行数等于左矩阵的行数, 乘积矩阵的列数等于右矩阵的列数。
例如设 $$ A=\left(\begin{array}{ll} 1 & 2 \ 3 & 4 \ 5 & 6 \end{array}\right), B=\left(\begin{array}{cc} 7 & 8 \ 9 & 10 \end{array}\right) $$
则:
矩阵的乘法有下面两条性质:
(1) 矩阵的乘法适合结合律,但一般不适合交换律。
设 $$A=\left(a_{ij}\right){m \times m}, B=\left(b{i j}\right){n \times s}, C=\left(c{i j}\right)_{s \times t}$$, 则
(2)矩阵的乘法适合左分配律,也适合右分配律: $$ A(B+C)=A B+A C,(B+C) D=B D+C D $$
特别地,如果
矩阵的乘法与数量乘法满足下述关系式:
矩阵的加法、数量乘法、乘法与矩阵的转置有如下关系:
**定义 1.4.8** 主对角线以外的元素全为 0 的方阵称为对角矩阵,简记作
定义 1.4 .9 对于数域
定义 1.4 .10 设
定义 1.5.1 设$$A$$是数域$$G$$上的$$n$$级矩阵,如果$$G^n$$中有非零列向量$$| {v}\rangle$$,使得
那么称$$v$$是$$A$$的一个特征值,称$$| {v}\rangle$$是$$A$$的属于特征值$$v$$的一个特征向量。
注意这里数值$$v$$和列向量$$| {v}\rangle$$是两个不同的概念,只不过为了突出它们之间的关系,都采用了$$v$$这个记号。
由线性代数中行列式及线性方程组的知识可知:
$ &\Leftrightarrow A|v\rangle=v|v\rangle,|v\rangle \neq 0, v \in G \$
$&\Leftrightarrow(v I-A)|v\rangle=0,|v\rangle \neq 0, v \in G \$
$\Leftrightarrow|v I-A|=0|v\rangle \text {是} (v I-A)|v\rangle=0 \text {的一个非零解}, v \in G$
$\Leftrightarrow v \text {是多项式} |\lambda I-A| \text {在} G \text{中的一个根},|v\rangle \text{是} (v I-A)|x\rangle=0 \text{的一个非零解} $
此处
设 $$ {v}$$ 是
定义 1.5.2 如果
定理 1.5.1 数域
这时, 令
证明 设
证毕。
定义 1.5 .3 若矩阵
定义 1.5 .4 若矩阵
易验证Hermite矩阵有如下性质:
(1)对于任意的向量
(2)$$(A B)^{\dagger}=B^{\dagger} A^{\dagger}$$,$$(A|v\rangle)^{\dagger}=\langle v| A^{\dagger}$$,$$\left(A^{\dagger}\right)^{\dagger}=A$$
定义 1.5 .5 矩阵
定义 1.5.6 矩阵
易看出酉矩阵有如下性质: $$ (U|v\rangle, U|w\rangle)=\left\langle v\left|U^{\dagger} U\right| w\right\rangle=\langle v \mid w\rangle $$
定理 1.5 .2 酉矩阵的所有特征值的模都是 1 。
证明 设
证毕。
定义 1.5.7 ^[61]^ 设有两个矩阵
为
如果
下面几条性质的证明比较简单,请读者自己思考。
(1) 若
(2)$$[A, B]^{\dagger}=\left[B^{\dagger}, A^{\dagger}\right],[A, B]=-[B, A]$$
(3) 设
下面不加证明地给出同时对角化定理。里面用到了一些线性代数的概念。
定理 1.5.3 (同时对角化定理) 设
类似于实数的函数,可以定义矩阵的函数。例如,在实数的多项式函数中只用戴高乐加法和幂运算,也可以类似地定义矩阵地多项式函数,这里的矩阵一般为方阵,因此需要用到幂运算。下面主要介绍矩阵(方阵)的指数函数。
定义 1.6.1 定义
这个定义相当于把
如果
类似于矩阵指数函数的定义,可以定义矩阵的其它函数。把矩阵代入其它函数的泰勒展开式即可。如矩阵的 正弦函数可定义为:
矩阵的余弦函数可定义为:
下面介绍一个很重要的欧拉公式:
这个公式其实是说等式两边的泰勒展式是相等的,下面来验证一下。先给出一些泰勒展式:
将以上三式代入欧拉公式等式两端,不难验证两端相等。
正比例函数
那么称
根据线性算子的定义可以验证以下性质: $$ \sigma\left(\sum_{i} a_{i}\left|v_{i}\right\rangle\right)=\sum_{i} a_{i} \sigma\left(\left|v_{i}\right\rangle\right) $$
通常
一个重要的线性算子是向量空间
另一个重要的线性算子是向量空间 V上的零算子0 (zero operator),它将
由于线性算子是映射的一种特殊情况,因此线性算子也可以做映射的合成,并满足合成的结合律。
最直观的理解线性算子的方式就是通过线性算子的矩阵表示 (matrix representations),因为矩阵能有一个 直观的认识 ^[58,60,61,62]^。
设
根据线性算子的定义可知,线性算子本质上是两个向量空间之间的映射,而映射表示一种对应关系。如果确 定空间V中每一个
而$V$中的每个元素都能被V中的基线性表示,因此将线性算子
由于
写成矩阵的形式
$$
\left[\sigma\left|v_{1}\right\rangle, \sigma\left|v_{2}\right\rangle, \cdots, \sigma\left|v_{n}\right\rangle\right]=\left[\left|w_{1}\right\rangle, \quad\left|w_{2}\right\rangle, \quad \cdots \quad,\left|w_{m}\right\rangle\right]\left[\begin{array}{cccc}
a_{11} & a_{12} & \cdots & a_{1 n} \
a_{21} & a_{22} & \cdots & a_{2 n} \
\vdots & \vdots & \ddots & \vdots \
a_{m 1} & a_{m 2} & \ldots & a_{m n}
\end{array}\right]
$$
把上式右端的
因此,对于给定基下的线性算子都可以找到与之对应的矩阵。并且这种矩阵表示方式是唯一的。
根据矩阵的运算法则以及线性算子的定义,也可以验证矩阵是一个线性算子。因此,在给定向量空间的基 下,线性算子与矩阵作用在同一向量空间上是等价的。
设
线性算子作用于向量空间中的元素所得到的新元素的坐标,实际上就是矩阵乘以向量空间中的元素的坐标。 因此前文矩阵的性质也对应于线性算子的性质。
根据线性算子的定义,可以将向量空间$V$中,两向量
而线性算子
一个n维向量可以看作一个1乘n或n乘1的矩阵,反过来,m乘n的矩阵可不可以看作一个向量呢? 本质上向量 和矩阵是一样的,只不过人们在不同的情况下,运用不同的表示方式。类比向量,矩阵的基又是什么呢? 矩 阵是一个线性映射,在给定基下,线性算子与矩阵等价,向量空间中的基与矩阵表示又有什么关系呢?
基于以上的疑问,下面引进向量外积的概念^[61]^。假设
这里借助于内积的运算定义了外积。
设在给定标准正价基下,向量
从而线性算子
$$ |w\rangle\langle v|=\left[\begin{array}{c}w_{1} \w_{2} \\vdots \w_{m}\end{array}\right]\left[\begin{array}{llll}v_{1}^{} & v_{2}^{} & \cdots & v_{n}^{}\end{array}\right]=\left[\begin{array}{cccc}w_{1} v_{1}^{} & w_{1} v_{2}^{} & \cdots & w_{1} v_{n}^{} \w_{2} v_{1}^{} & w_{2} v_{2}^{} & \cdots & w_{2} v_{n}^{} \\vdots & \vdots & \ddots & \vdots \w_{m} v_{1}^{} & w_{m} v_{2}^{} & \cdots & w_{m} v_{n}^{}\end{array}\right] \text {. } $$
可以看出,在给定标准正交基下,线性算子
向量空间$V$上的线性算子$A$的对角表示 (diagonal representation) ^[60,61]^ 是指$A$可以表示成
其中向量
若一个线性算子有对角表示,则该线性算子一定可对角化的 (diagonalizable)。比如,Pauli Z矩阵有对角表示 $$ Z=\left[\begin{array}{cc} 1 & 0 \ 0 & -1 \end{array}\right]=1 \cdot|0\rangle\langle 0|+(-1) \cdot| 1\rangle\langle 1| $$
而线性算子可对角化,不一定有对角表示。
比如,矩阵 $$ \left[\begin{array}{ll} 1 & -2 \ 0 & -1 \end{array}\right] $$
特征值1对应的特征向量为 $$k_{1}\left[\begin{array}{cc}1 & 0\end{array}\right]^{T}$$ ,特征值-1对应的特征向量为 $$k_{2}\left[\begin{array}{cc}1 & 1\end{array}\right]^{T}$$ ,这两个特征向量并不正交, 因此不可以对角表示。
而矩阵 $$ \left[\begin{array}{ll} 1 & 0 \ 1 & 1 \end{array}\right] $$
是不可对角化的。
定理 向量空间V上任意线性算子A是正规算子的充要条件是在
假设
为子空间W上的投影算子 (projector) ^[61]^。定义
为投影算子P的正交补 (orthogonal complement )。可以验证
定理 对于任意投影
证明
因为
$$\langle i \mid j\rangle=\delta_{i j}=\left{\begin{array}{l}1,(i=j) \ 0,(i \neq j)\end{array}\right.$$
证毕。
一种功能齐全,运行高效的量子软件开发工具包。
QPanda 2是由本源量子开发的开源量子计算框架,它可以用于构建、运行和优化量子算法。QPanda 2作为本源量子计算系列软件的基础库,为QRunes、Qurator、量子计算服务提供核心部件。
QPanda 2是以C++为宿主语言,其对系统的环境要求如表附2.1.1:
软件 | 版本 |
---|---|
CMake | >= 3.1 |
GCC | >= 5.0 |
Python | >= 3.6.0 |
如果在您的系统上已经安装了git, 你可以直接输入以下命令来获取QPanda 2:
$ git clone https://github.com/OriginQ/QPanda 2.git
对于一些为安装git的用户来说,也可以直接通过浏览器去下载QPanda 2, 具体的操作步骤如下:
(1)在浏览器中输入 https://github.com/OriginQ/QPanda 2 ,进入网页会看到:
图附2.1.1 Github界面 (2)点击 Clone or download 看到界面:
图附2.1.2 Github界面 (3)然后点击 Download ZIP, 就会完成QPanda 2的下载。
图附2.1.3 下载 我们支持在Windows、Linux、MacOS下构建QPanda 2。用户可以通过CMake的方式来构建QPanda 2。
1)Windows
在Windows下构建QPanda 2。用户首先需要保证在当前主机下安装了CMake环境和C++编译环境,用户可以通过Visual Studio和MinGW方式编译QPanda 2。
(1)使用Visual Studio
这里以Visual Studio 2017为例,使用Visual Studio 2017 编译QPanda 2,只需要安装Visual Studio 2017,并需要在组件中安装CMake组件。安装完成之后,用Visual Studio 2017打开QPanda 2文件夹,即可使用CMake编译QPanda 2。
图附2.1.4 使用CMake编译QPanda 2 (2)使用MinGW
使用MinGW编译QPanda 2,需要自行搭建CMake和MinGW环境,用户可自行在网上查询环境搭建教程。(注意: MinGW需要安装64位版本)
CMake+MinGW的编译命令如下:
①在QPanda 2根目录下创建build文件夹;
②进入build文件夹,打开cmd;
③由于MinGW对CUDA的支持存在一些问题,所以在编译时需要禁掉CUDA,输入以下命令:
1.cmake -G"MinGW Makefiles" -DFIND_CUDA=OFF -DCMAKE_INSTALL_PREFIX=C:/QPanda2 ..
2.mingw32-make
2)Linux 和MacOS
在Linux和MacOS下编译QPanda 2,命令是一样的。
编译步骤如下:
①进入QPanda 2根目录;
②输入以下命令:
1.mkdir -p build
2.cd build
3.cmake ..
4.make
如果有需求,用户通过命令修改QPanda 2的安装路径,配置方法如下所示:
1.mkdir -p build
2.cd build
3.cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
4.make
QPanda 2编译完成后,会以库的形式存在。为了方便调用,大家可以把QPanda 2库安装到指定位置,安装的方式如下所示。
1)Windows
(1)使用Visual Studio
同样以Visual Studio 2017为例,在QPanda 2编译完成后,用户可以安装,Visual Studio 2017的安装方式很简单,只需要在Cmake菜单中选择“安装”即可。
图附2.1.5 在Cmake菜单中选择“安装” QPanda 2会安装在用户在CMakeSettings.json中配置的安装目录下。安装成功后会在用户配置的的目录下生成install文件夹,里面安装生成include和lib文件。如果有需求,用户可以在Visual Studio的CMakeSettings.json配置文件修改的安装路径。生成CMakeSettings.json的方法如下图所示:
图附2.1.6 生成CMakeSettings.json的方法 修改QPanda 2的安装路径如下图所示:
图附2.1.7 修改QPanda 2的安装路径 参数修改完成后,cmake选项下执行安装,QPanda 2的lib库文件和include头文件会安装到用户指定的安装位置。(注意:需先进行编译成功后才能进行安装)
(2)使用MinGW
在QPanda 2编译完成后,用户可以安装QPanda 2,安装命令如下:
1.mingw32-make install
2)Linux 和MacOS
在Linux和MacOS下安装命令QPanda 2,命令是一样的,安装命令如下:
1.sudo make install
使用不同的平台和不同的IDE在构建C++项目是的方法是不一样的,调用库的方式也不尽相同,大家可以选择用自己的方式调用QPanda 2库, 下面我们以cmake构建项目为例,演示调用QPanda 2库进行量子编程。
(1)Visual Studio调用QPanda 2库
Visual Studio下调用QPanda 2库的CMakeList的写法为
1.cmake_minimum_required(VERSION 3.1)
2.project(testQPanda)
3.SET(CMAKE_INSTALL_PREFIX "C:/QPanda2") # QPanda2安装的路径
4.SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_INSTALL_PREFIX}/lib/cmake")
5.
6.set(CMAKE_CXX_STANDARD 14)
7.set(CMAKE_CXX_STANDARD_REQUIRED ON)
8.if (NOT USE_MSVC_RUNTIME_LIBRARY_DLL)
9. foreach (flag
10. CMAKE_C_FLAGS
11. CMAKE_C_FLAGS_DEBUG
12. CMAKE_C_FLAGS_RELEASE
13. CMAKE_C_FLAGS_MINSIZEREL
14. CMAKE_C_FLAGS_RELWITHDEBINFO
15. CMAKE_CXX_FLAGS
16. CMAKE_CXX_FLAGS_DEBUG
17. CMAKE_CXX_FLAGS_RELEASE
18. CMAKE_CXX_FLAGS_MINSIZEREL
19. CMAKE_CXX_FLAGS_RELWITHDEBINFO)
20.
21. if (${flag} MATCHES "/MD")
22. string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
23. endif()
24. if (${flag} MATCHES "/MDd")
25. string(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
26. endif()
27. if (${flag} MATCHES "/W3")
28. string(REGEX REPLACE "/W3" "/W0" ${flag} "${${flag}}")
29. endif()
30. endforeach()
31.endif()
32.
33.set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
34.set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
35.
36.find_package(OpenMP)
37.if(OPENMP_FOUND)
38. option(USE_OPENMP "find OpenMP" ON)
39. message("OPENMP FOUND")
40. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
41. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
42. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
43.else(OPENMP_FOUND)
44. option(USE_OPENMP "not find OpenMP" OFF)
45.endif(OPENMP_FOUND)
46.
47.find_package(QPANDA REQUIRED)
48.if (QPANDA_FOUND)
49. include_directories(${QPANDA_INCLUDE_DIR})
50.endif (QPANDA_FOUND)
51.
52.add_executable(${PROJECT_NAME} test.cpp)
53.target_link_libraries(${PROJECT_NAME} ${QPANDA_LIBRARIES})
(2)MinGW调用QPanda 2库
MinGW调用QPanda 2库的CMakeList的写法为
1.cmake_minimum_required(VERSION 3.1)
2.project(testQPanda)
3.SET(CMAKE_INSTALL_PREFIX "C:/QPanda2") # QPanda2安装的路径
4.SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_INSTALL_PREFIX}/lib/cmake")
5.
6.
7.add_definitions("-w -DGTEST_USE_OWN_TR1_TUPLE=1")
8.set(CMAKE_BUILD_TYPE "Release")
9.set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -g -ggdb")
10.set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3")
11.add_compile_options(-fpermissive)
12.
13.set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
14.set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
15.
16.find_package(OpenMP)
17.if(OPENMP_FOUND)
18. option(USE_OPENMP "find OpenMP" ON)
19. message("OPENMP FOUND")
20. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
21. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
22. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
23.else(OPENMP_FOUND)
24. option(USE_OPENMP "not find OpenMP" OFF)
25.endif(OPENMP_FOUND)
26.
27.find_package(QPANDA REQUIRED)
28.if (QPANDA_FOUND)
29. include_directories(${QPANDA_INCLUDE_DIR})
30.endif (QPANDA_FOUND)
31.
32.add_executable(${PROJECT_NAME} test.cpp)
33.target_link_libraries(${PROJECT_NAME} ${QPANDA_LIBRARIES})
(3)Linux、MacOS下使用QPanda2
Linux、MacOS使用QPanda2的方式是相同的,其CmakeList.txt的写法为:
1.cmake_minimum_required(VERSION 3.1)
2.project(testQPanda)
3.SET(CMAKE_INSTALL_PREFIX "/usr/local") # QPanda2安装的路径
4.SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_INSTALL_PREFIX}/lib/cmake")
5.
6.
7.add_definitions("-w -DGTEST_USE_OWN_TR1_TUPLE=1")
8.set(CMAKE_BUILD_TYPE "Release")
9.set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -g -ggdb")
10.set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3")
11.add_compile_options(-fpermissive)
12.
13.set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
14.set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
15.
16.find_package(OpenMP)
17.if(OPENMP_FOUND)
18. option(USE_OPENMP "find OpenMP" ON)
19. message("OPENMP FOUND")
20. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
21. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
22. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
23.else(OPENMP_FOUND)
24. option(USE_OPENMP "not find OpenMP" OFF)
25.endif(OPENMP_FOUND)
26.
27.find_package(QPANDA REQUIRED)
28.if (QPANDA_FOUND)
29. include_directories(${QPANDA_INCLUDE_DIR})
30.endif (QPANDA_FOUND)
31.
32.add_executable(${PROJECT_NAME} test.cpp)
33.target_link_libraries(${PROJECT_NAME} ${QPANDA_LIBRARIES})
注解:
test.cpp 为使用QPanda2的一个示例。有兴趣的可以试着将其合并在一起形成一个跨平台的CMakeList.txt。
通过一个示例介绍QPanda 2的使用,下面的例子可以在量子计算机中构建量子纠缠态(|00>+|11>),对其进行测量,重复制备1000次。 预期的结果是约有50%的概率使测量结果分别在00或11上。
1.#include "QPanda.h"
2.using namespace QPanda;
3.int main()
4.{
5. auto qvm = CPUQVM();
6. auto prog = QProg();
7. auto q = qvm.qAllocMany(2);
8. auto c = qvm.cAllocMany(2);
9. prog << H(q[0])
10. << CNOT(q[0],q[1])
11. << MeasureAll(q, c);
12. auto results = qvm.runWithConfiguration(prog, c, 1000);
13. for (auto result : results){
14. std::cout << result.first << " : " << result.second << std::endl;
15. }
16. return 0;
17.}
编译方式与编译QPanda库的方式基本类似,在这里就不多做赘述。
编译之后的可执行文件会生成在build下的bin文件夹中,进入到bin目录下就可以执行自己编写的量子程序了。
计算结果如下所示:
1.00 : 493
2.11 : 507
我们通过pybind11工具,以一种直接和简明的方式,对QPanda2中的函数、类进行封装,并且提供了几乎完美的映射功能。 封装部分的代码在QPanda2编译时会生成为动态库,从而可以作为Python的包引入。
pyqpanda是以C++为宿主语言,其对系统的环境要求如表附2.2.1:
软件 | 版本 |
---|---|
GCC | >= 5.4.0 |
Python | >= 3.6.0 |
如果你已经安装好了Python环境和pip工具, 在终端或者控制台输入下面命令:
pip install pyqpanda
注解
在linux下若遇到权限问题需要加 sudo
我们提供了Linux,Windows,MacOS上的python预编译包供安装,需要python==3.8。还需要numpy>=1.18.5,pyqpanda>=3.7.8。
pip install pyvqnet
VQNet 测试安装成功
import pyvqnet
from pyvqnet.tensor import *
a = arange(1,25).reshape([2, 3, 4])
print(a)
这里使用VQNet的经典神经网络模块以及量子模块完成一个机器学习模型的整体流程的例子介绍。该例子参考 Data re-uploading for a universal quantum classifier 。 量子机器学习中的量子计算模块一般有如下几个部分:
(1)编码线路(Encoder),用于将经典数据编码到量子数据; (2)可变参数的量子线路(Ansatz),用于训练带参量子门中的参数; (3)测量模块(Measurement),用于检测测量值(也就是某个量子比特的量子态在某些轴上的投影)。
量子计算模块与经典神经网络的运算模块一样是可微分的,是量子经典神经网络混合模型的理论基础。 VQNet支持将量子计算模块与经典计算模块(例如:卷积,池化,全连接层,激活函数等)一起构成混合机器学习模型,提供多种优化算法优化参数。
在量子计算模块,VQNet支持使用本源量子高效的量子软件计算包 pyQPanda 进行量子模块构建。 使用pyQPanda提供的各种常用 量子逻辑门函数接口 , 量子线路接口 , 量子虚拟机函数接口 , 测量函数接口,用户可以快速构建量子计算模块。
接下来的例子我们使用pyQPanda构建了一个量子计算模块。通过VQNet,该量子计算模块可以直接嵌入到混合机器学习模型中进行量子线路参数训练。 本例使用1个量子比特,使用了多个带参数的旋转门 RZ,RY,RZ 对输入x进行编码,并使用 prob_run_dict() 函数观测量子比特的概率测量结果作为输出。
def qdrl_circuit(input,weights,qlist,clist,machine):
x1 = input.squeeze()
param1 = weights.squeeze()
#使用pyqpanda接口构建量子线路实例
circult = pq.QCircuit()
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位x1[0]
circult.insert(pq.RZ(qlist[0], x1[0]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RY门,参数位x1[1]
circult.insert(pq.RY(qlist[0], x1[1]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位x1[2]
circult.insert(pq.RZ(qlist[0], x1[2]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位param1[0]
circult.insert(pq.RZ(qlist[0], param1[0]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RY门,参数位param1[1]
circult.insert(pq.RY(qlist[0], param1[1]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位param1[2]
circult.insert(pq.RZ(qlist[0], param1[2]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位x1[0]
circult.insert(pq.RZ(qlist[0], x1[0]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RY门,参数位x1[1]
circult.insert(pq.RY(qlist[0], x1[1]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位x1[2]
circult.insert(pq.RZ(qlist[0], x1[2]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位param1[3]
circult.insert(pq.RZ(qlist[0], param1[3]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RY门,参数位param1[4]
circult.insert(pq.RY(qlist[0], param1[4]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位param1[5]
circult.insert(pq.RZ(qlist[0], param1[5]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位x1[0]
circult.insert(pq.RZ(qlist[0], x1[0]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RY门,参数位x1[1]
circult.insert(pq.RY(qlist[0], x1[1]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位x1[2]
circult.insert(pq.RZ(qlist[0], x1[2]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位param1[6]
circult.insert(pq.RZ(qlist[0], param1[6]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RY门,参数位param1[7]
circult.insert(pq.RY(qlist[0], param1[7]))
#使用pyqpanda接口在第一个量子比特上插入逻辑门RZ门,参数位param1[8]
circult.insert(pq.RZ(qlist[0], param1[8]))
#构建量子程序
prog = pq.QProg()
prog.insert(circult)
#获取概率测量值
prob = machine.prob_run_dict(prog, qlist, -1)
prob = list(prob.values())
return prob
本例子中机器学习的任务是对随机生成的数据根据进行二分类,其中下图是该数据样例,零为圆点,半径为1以内红色的二维点为一类,蓝色的点为另一类。
训练测试代码流程
#导入必须的库和函数
from pyvqnet.qnn.qdrl.vqnet_model import qdrl_circuit
from pyvqnet.qnn.quantumlayer import QuantumLayer
from pyvqnet.optim import adam
from pyvqnet.nn.loss import CategoricalCrossEntropy
from pyvqnet.tensor import QTensor
import numpy as np
from pyvqnet.nn.module import Module
定义模型Model,其中 init 函数定义内部各个神经网络模块以及量子模块,forward 函数定义前传函数。QuantumLayer 为封装量子计算的抽象类。 您只需将刚才定义的量子计算函数 qdrl_circuit,待训练参数个数 param_num ,运行后端配置 “cpu” , 量子比特数 qbit_num 输入参数,该类就在 VQNet 中自动计算参数梯度。
#待训练参数个数
param_num = 9
#量子计算模块量子比特数
qbit_num = 1
#定义一个继承于Module的机器学习模型类
class Model(Module):
def __init__(self):
super(Model, self).__init__()
#使用QuantumLayer类,可以把带训练参数的量子线路纳入VQNet的自动微分的训练流程中
self.pqc = QuantumLayer(qdrl_circuit,param_num,"cpu",qbit_num)
#定义模型前向函数
def forward(self, x):
x = self.pqc(x)
return x
定义一些训练模型需要的函数
# 随机产生待训练数据的函数
def circle(samples:int, rads = np.sqrt(2/np.pi)) :
data_x, data_y = [], []
for i in range(samples):
x = 2*np.random.rand(2) - 1
y = [0,1]
if np.linalg.norm(x) < rads:
y = [1,0]
data_x.append(x)
data_y.append(y)
return np.array(data_x), np.array(data_y)
# 数据载入函数
def get_minibatch_data(x_data, label, batch_size):
for i in range(0,x_data.shape[0]-batch_size+1,batch_size):
idxs = slice(i, i + batch_size)
yield x_data[idxs], label[idxs]
#计算准确率的函数
def get_score(pred, label):
pred, label = np.array(pred.data), np.array(label.data)
pred = np.argmax(pred,axis=1)
score = np.argmax(label,1)
score = np.sum(pred == score)
return score
VQNet遵循机器学习一般的训练测试流程: 迭代进行载入数据,前传计算,损失函数计算,反向计算,更新参数的操作。
#实例化定义的模型
model = Model()
#定义一个优化器,这里用的是Adam
optimizer = adam.Adam(model.parameters(),lr =0.6)
#定义一个损失函数,这里用的交叉熵损失函数
Closs = CategoricalCrossEntropy()
训练模型部分的函数
def train():
# 随机产生待训练数据
x_train, y_train = circle(500)
x_train = np.hstack((x_train, np.zeros((x_train.shape[0], 1))))
# 定义每个批次训练的数据个数
batch_size = 32
# 最大训练迭代次数
epoch = 10
print("start training...........")
for i in range(epoch):
model.train()
accuracy = 0
count = 0
loss = 0
for data, label in get_minibatch_data(x_train, y_train,batch_size):
# 优化器中缓存梯度清零
optimizer.zero_grad()
# 模型前向计算
output = model(data)
# 损失函数计算
losss = Closs(label, output)
# 损失反向传播
losss.backward()
# 优化器参数更新
optimizer._step()
# 计算准确率等指标
accuracy += get_score(output,label)
loss += losss.item()
count += batch_size
print(f"epoch:{i}, train_accuracy:{accuracy/count}")
print(f"epoch:{i}, train_loss:{loss/count}\n")
验证模型部分的函数
def test():
batch_size = 1
model.eval()
print("start eval...................")
xtest, y_test = circle(500)
test_accuracy = 0
count = 0
x_test = np.hstack((xtest, np.zeros((xtest.shape[0], 1))))
predicted_test = []
for test_data, test_label in get_minibatch_data(x_test,y_test, batch_size):
test_data, test_label = QTensor(test_data),QTensor(test_label)
output = model(test_data)
test_accuracy += get_score(output, test_label)
count += batch_size
print(f"test_accuracy:{test_accuracy/count}")
训练测试结果图:
start training...........
epoch:0, train_accuracy:0.6145833333333334
epoch:0, train_loss:0.020432369535168013
epoch:1, train_accuracy:0.6854166666666667
epoch:1, train_loss:0.01872217481335004
epoch:2, train_accuracy:0.8104166666666667
epoch:2, train_loss:0.016634768371780715
epoch:3, train_accuracy:0.7479166666666667
epoch:3, train_loss:0.016975031544764835
epoch:4, train_accuracy:0.7875
epoch:4, train_loss:0.016502128106852372
epoch:5, train_accuracy:0.8083333333333333
epoch:5, train_loss:0.0163204787299037
epoch:6, train_accuracy:0.8083333333333333
epoch:6, train_loss:0.01634311651190122
epoch:7, train_loss:0.016330583145221074
epoch:8, train_accuracy:0.8125
epoch:8, train_loss:0.01629052646458149
epoch:9, train_accuracy:0.8083333333333333
epoch:9, train_loss:0.016270687493185203
start eval...................
test_accuracy:0.826
一种基于VS Code的量子程序开发工具。
qurator-vscode 是本源量子推出的一款可以开发量子程序的 VS Code 插件。其支持 QRunes2 语言量子程序开发,并支持 Python 和 C++ 语言作为经典宿主语言。
在 qurator-vscode 中,量子程序的开发主要分为编写和运行两个部分。
·编写程序:插件支持模块化编程,在不同的模块实现不同的功能,其中量子程序的编写主要在 qcodes 模块中;
·程序运行:即是收集结果的过程,插件支持图表化数据展示,将运行结果更加清晰的展现在您的面前。
考虑到目前量子程序的开发离不开经典宿主语言的辅助,qurator-vscode 插件设计时考虑到一下几点:
(1)模块编程:
qurator-vscode 插件支持模块编程,将整体程序分为三个模块:settings、qcodes 和 script 模块。在不同的模块完成不同的功能。 在 settings 模块中,您可以进行宿主语言类型、编译还是运行等设置;在 qcodes 模块中, 您可以编写 QRunes2 语言程序; 在 script 模块中,您可以编写相应的宿主语言程序。
(2)切换简单:
qurator-vscode 插件目前支持两种宿主语言,分别为 Python 和 C++。您可以在两种宿主语言之间自由的切换,您只需要在 settings 模块中设置 language 的 类型,就可以在 script 模块中编写对应宿主语言的代码。插件会自动识别您所选择的宿主语言,并在 script 模块中提供相应的辅助功能。
(3)图形展示:
qurator-vscode 插件提供图形化的结果展示,程序运行后会展示 json 格式的运行结果,您可以点击运行结果,会生成相应的柱状图,方便您对运行结果的分析。
使用 qurator-vscode 插件之前需要做一些准备工作,以确保量子程序能够正确的运行。
需要依赖的运行环境有:
·Python (版本 3.6.4 - 3.6.8)
·Pip (版本 10.1 及以上)
·Microsoft Visual C++ Redistributable (Windows)
·MinGw-w64 (Windows 64位版本)
其中,pip 负责下载宿主语言为 Python 时程序运行所依赖的包。Microsoft Visual C++ Redistributable 和 MinGw-w64 是宿主语言为 C++ 时程序运行所依赖的包。
(1)安装插件
首先需要您安装 VS Code ,然后打开 VS Code 安装 qurator-vscode 插件: 使用 Ctrl + Shift + X 快捷键打开插件页面,或者您可以在最左侧栏找到 Extensions 点击进入,然后输入 qurator-vscode 来搜索插件,点击 Install 按钮进行插件的安装。
图附2.4.1 插件安装 (2)检测运行环境
插件安装好之后,您可以创建以 .qrunes 结尾的文件,此时插件会自动检测是否存在程序运行所依赖的环境。您也可以自己检测程序运行环境,使用 Ctrl + Shift + P 快捷键打开 VS Code 命令行, 输入 qurator-vscode 时您可以看到 qurator-vscode: Check Qurator VSCode Extension dependencies 选择项,点击此项就可以进行运行环境的检测。
图附2.4.2 运行环境检测 检测到运行时所需环境,会在右下角展示软件及版本号:
图附2.3.3 软件及版本号 在做好准备工作之后,下面就可以编写属于您自己的量子程序了。
(1)项目文件夹中启动 VS Code
在命令提示符或终端上,创建一个名为 “test” 的空文件夹,切换到该文件夹,然后输入命令 code . 在该文件夹中打开 VS Code:
mkdir test
cd test
code .
或者,您可以点击运行 VS Code,然后点击 “File” > “Open File…” 打开项目文件夹。在文件夹中启动 VS Code,该文件夹将成为您的“工作区”。您可以在 .vscode/settings.json 文件中更改工作区的相关设置。
(2)创建一个 qrunes 文件
在文件资源管理器工具栏中,单击 “test” 文件夹上的 “New File” 按钮,并命名该文件为 qurator_test.qrunes。
图附2.4.4 创建 qrunes 文件 (3)编写量子程序
qrunes 文件创建完成之后,便可以编写量子程序了。整个量子程序分为三个部分:settings、qcodes 和 script 三个模块。
其中,settings 模块中可以设置宿主语言,编译还是运行;qcodes 模块中可以编写 QRunes2 量子语言代码; script 模块中可以编写宿主语言代码,目前支持 Python 和 C++ 两种宿主语言。
图附2.4.5 编写量子程序 (4)编译运行
点击右上方 Run this QRunes 运行程序,或者使用命令提示符 qurator-vscode: Run this QRunes 来运行程序(快捷键 F5):
图附2.4.6 编译运行 上述示例程序的运行结果如图附2.3.7:
图附2.4.7 运行结果 相信在快速入门步骤之后,您已大体了解插件的整体功能,下面将介绍您在编辑量子程序过程中插件提供的辅助功能:
(1)自动补全
对于 QRunes2 语言内设的关键字可以智能提示,根据输入的字符列为您提供当前上下文中适用的最相关符号列表并提示其功能, 以便您可以更快地选择。
图附2.4.8 自动补全 (2)验证提示
对于输入进行验证并提示。每当插件检测到您编写的代码发生语法错误时,编辑器中会显示红色波浪线, 鼠标放上去可看到一系列错误信息,您可以准确定位错误发生的位置。
图附2.4.9 验证提示 (3)高亮展示
不同的模块有不同的颜色划分,您可以清晰地编写每一个模块的代码,一目了然,快速开发。
图附2.4.10 高亮展示 (4)悬浮提示
QRunes2 语言中的方法、变量都有详细的解释及用法。每当您编写 QRunes2 语言内设关键字时, 将鼠标放在该关键字上,编辑器将会显示该关键字的功能信息。
图附2.4.11 悬浮提示 (5)智能片段
智能片段功能是指用户输入简短的触发指令而生成完整的代码片段,在本插件中内置了自定义代码片段, 可帮助您整理一些重复性代码,提高开发效率。
图附2.4.12 智能片段 (6)语言切换
目前 QRunes2 语言可以支持 Python 及 C++ 宿主语言,您可以在 settings 模块的 language 关键字来设置 所需支持的语言类型,就可以在script模块编写相应语言的代码。
图附2.4.13 语言切换 (7)编译运行
运行 QRunes2 语言代码,编译器会根据设定的语言去编译该代码,从而实现不同的语言编写生成相同的结构。
图附2.4.14 编译运行 ChemiQ是在量子计算机或虚拟机上模拟化学分子结构和性质的仿真软件——也是全球首款运用量子算法模拟的仿真软件——接入量子计算机计算速度呈指数增长。ChemiQ利用Jordan-Wigner,Parity等方法将二次量子化的Fermion的Hamiltonian算符转化(mapping)成Qubit的Hamiltonian算符(量子计算机识别的算符),算符间的转换是量子计算模拟化学过程的第一步,不同的转化方法对应着不同的Qubit信息,研究出所转化的算符少的mapping,相应的计算少,可大大简化计算;使用Unitary Coupled Cluster(简称UCC)等拟设构造模拟量子电路,分别代表不同的电路模型,所包含的参数数目和线路深度也不尽相同,构造出参数少、线路浅的线路拟设是量子计算模拟的关键,使得复杂的化学过程得到有效模拟;再结合量子相位评估(QPE),变分量子本征求解(VQE)算法,或量子虚时演化(QITE)算法模拟分子哈密顿量的期望值,进一步预测分子性质。这些算法不仅能保证量子态的相干性,其计算结果还能达到化学精度,在可预见的未来,有着极大的应用前景和优势。
图附3.1.1 ChemiQ软件 1、登录本源量子云官网(https://qcloud.originqc.com.cn/),选择“应用推广云”,如图附3.1.2 所示;选择“生物医药”方向,点击“了解详情”如图附3.1.3所示;点击“Windows进行下载”,如图附3.1.4所示。(直达页面:https://qcloud.originqc.com.cn/chemistryIntroduce)
图附3.1.2 选择“应用推广云” 图附3.1.3 选择“生物医药” 图附3.1.3 下载 2、下载完成后,双击安装该软件,点击“我同意”;
图附3.1.4 点击“我同意” 3、然后点击“下一步”;
图附3.1.5 下一步 4、再安装到默认目录,点击“安装”;
图附3.1.6 点击“安装” 5、安装完成后,运行该软件。
图附3.1.7 安装完成 1、 新建项目。先新建一个项目名称——test H2,然后是填好创建人、计算模式、保存路径和项目描述,最后点击确定。
图附3.2.1 新建项目 2、 主界面。该软件左侧显示的是项目下的任务列表;左下显示的是项目或者任务详情;右侧显示分为上下两部分,上部分为分子模型,下部分为结果展示,可上下拖动调节大小。
图附3.2.2 主页面 3、 分子建模。以氢分子势能曲线为例,首先是构建分子模型。点击设置-分子模型,或者工具栏中图标构建分子模型。在这里可使用构建分子快捷工具,如右侧弹出框。
图附3.2.3 分子建模 4、 配置参数。在参数配置框中选择计算类型——势能曲线,基组、电荷、自旋多重度可根据用户对应设置,在这里以STO-3G基组模拟氢分子PES;扫描坐标选择氢分子中键长距离为变量,设置对应的节点个数和扫描区间;映射、拟设和优化器对应选择如下图所示:
图附3.2.4 配置参数 5、任务详情。左下部分显示任务的参数详情,如计算结果中展示的计算列表,其中往下拉会显示20个节点任务;此外为了便于显示,可点击视图中便捷工具,如图展示是分子模型中的元素名称和元素编号。
图附3.2.5 任务详情 6、 计算完成。可以看到计算结果已经展示出来,同时也可以看到每个氢分子坐标计算得到能量如下:
图附3.2.6 计算完成 7、 结果展示。势能曲线中可点击单个节点进入结果详情中,或者点击计算列表/势能曲线切换结果展示。
图附3.2.7 结果展示使用封装的ChemiQ计算接口进行实现
表附3.3.1 ChemiQ封装的计算接口接口名称 | 描述 |
---|---|
initialize | 初始化量子化学计算的环境 |
finalize | 释放量子化学计算的环境 |
setMolecule | 设置单个分子模型 |
setMolecules | 设置一组分子模型 |
setMultiplicity | 设置重数 |
setCharge | 设置电荷数 |
setBasis | 设置计算基 |
setTransformType | 设置费米子到泡利算子的转换类型 |
setUccType | 设置UCC模型类型 |
setOptimizerType | 设置优化器类型 |
setOptimizerIterNum | 设置优化器迭代次数 |
setOptimizerFuncCallNum | 设置优化器函数调用次数 |
setOptimizerXatol | 设置优化器参数收敛阈值 |
setOptimizerFatol | 设置优化器函数收敛阈值 |
setLearningRate | 设置学习率 |
setEvolutionTime | 设置演化时间 |
setHamiltonianSimulationSlices | 设置哈密顿量模拟切片数 |
setSaveDataDir | 设置中间数据存放目录 |
setRandomPara | 设置随机优化参数 |
setDefaultOptimizedPara | 设置默认优化参数 |
setToGetHamiltonianFromFile | 设置从文件获取体系哈密顿量 |
setHamiltonianGenerationOnly | 设置只生成体系哈密顿量 |
exec | 执行计算 |
getLastError | 获取最后一条错误日志 |
initialize接口,作用是用来初始化量子化学计算环境,它需要传入一个变量就是量子化学计算包的路径,这里已经把PSi4安装在了python能检索到的环境路径下,使用时只需要传入空的字符串即可。 下面演示一下如何使用ChemiQ计算接口来实现氢分子的基态能量计算。
首先构造一组不同距离下的氢分子模型;然后生成ChemiQ的一个实例,调用setMolecules接口设置一组氢分子模型,设置氢分子的电荷数为0,自旋多重度为1;使用的计算基是sto-3g;UCC模型我们使用的是UCCS,费米子哈密顿量到泡利哈密顿量的转换这里用的是JW变换;这里使用的优化器是Nelder-Mead,优化器迭代次数为200,函数调用次数为200,显示优化器计算的中间结果;最后执行计算。
1.import matplotlib.pyplot as plt
2.
3.from pyqpanda import *
4.
5.if __name__=="__main__":
6.
7. distances = [x * 0.1 for x in range(2, 25)]
8. molecule = "H 0 0 0\nH 0 0 {0}"
9.
10. molecules = []
11. for d in distances:
12. molecules.append(molecule.format(d))
13.
14. chemiq = ChemiQ()
15. chemiq.initialize("")
16. chemiq.setMolecules(molecules)
17. chemiq.setCharge(0)
18. chemiq.setMultiplicity(1)
19. chemiq.setBasis("sto-3g")
20. chemiq.setUccType(UccType.UCCS)
21. chemiq.setTransformType(TransFormType.Jordan_Wigner)
22. chemiq.setOptimizerType(OptimizerType.NELDER_MEAD)
23. chemiq.setOptimizerIterNum(200)
24. chemiq.setOptimizerFatol(200)
25. chemiq.exec()
26. chemiq.finalize()
27.
28. value = chemiq.getEnergies()
29.
30. plt.plot(distances , value, 'r')
31. plt.xlabel('distance')
32. plt.ylabel('energy')
33. plt.title('VQE PLOT')
34. plt.show()
获取优化后的能量,绘制曲线图。这条曲线就是优化得到的氢分子在不同距离下对应的基态能量:
图附3.3.1 氢分子在不同距离下对应的基态能量 首先,导入pyQPanda和psi4_wrapper中的所有模块,以及一些其它组件模块准备。
1.from pyqpanda import *
2.from psi4_wrapper import *
3.import numpy as np
4.from functools import partial
5.from math import pi
6.import matplotlib.pyplot as plt
然后,定义非梯度下降优化器使用的损失函数为loss_func,loss_func接受的一组参数为待优化的参数列表,轨道个数,电子个数,体系哈密顿量。这个接口是先用ccsd模型构造的费米子哈密顿量,然后利用JW变换将费米子哈密顿量转换为泡利哈密顿量,再接着将CC转化成UCC,再计算体系哈密顿量在试验态下的期望,最后返回期望值来实现的。
1.def loss_func(para_list, qubit_number, electron_number, Hamiltonian):
2. '''
3. <𝜓^∗|𝐻|𝜓>, Calculation system expectation of Hamiltonian in experimental state.
4. para_list: parameters to be optimized
5. qubit_number: qubit number
6. electron_number: electron number
7. Hamiltonian: System Hamiltonian
8. '''
9. fermion_cc =get_ccsd(qubit_number, electron_number, para_list)
10. pauli_cc = JordanWignerTransform(fermion_cc)
11. ucc = cc_to_ucc_hamiltonian(pauli_cc)
12. expectation=0
13. for component in Hamiltonian:
14. expectation+=get_expectation(qubit_number, electron_number, ucc, component)
15. expectation=float(expectation.real)
16. print(expectation)
17. return ("", expectation)
下面将对loss_func使用到的接口逐个进行讲解:
get_ccsd_n_term接口的作用是返回构造CCSD模型需要用到的参数个数,这个接口接收的参数是轨道个数和电子个数。
1.def get_ccsd_n_term(qn, en):
2. '''
3. coupled cluster single and double model.
4. e.g. 4 qubits, 2 electrons
5. then 0 and 1 are occupied,just consider 0->2,0->3,1->2,1->3,01->23
6. '''
7.
8. if n_electron>n_qubit:
9. assert False
10.
11. return int((qn - en) * en + (qn - en)* (qn -en - 1) * en * (en - 1) / 4)
get_ccsd接口则是用来构造普通参数对应的CCSD模型费米子哈密顿量,该接口接收的参数是轨道个数、电子个数和单激发双激发前面对应的系数。
1.def get_ccsd(qn, en, para):
2. '''
3. get Coupled cluster single and double model.
4. e.g. 4 qubits, 2 electrons
5. then 0 and 1 are occupied,just consider 0->2,0->3,1->2,1->3,01->23.
6. returned FermionOperator like this:
7. { {"2+ 0":var[0]},{"3+ 0":var[1]},{"2+ 1":var[2]},{"3+ 1":var[3]},
8. {"3+ 2+ 1 0":var[4]} }
9.
10. '''
11. if n_electron>n_qubit:
12. assert False
13. if n_electron==n_qubit:
14. return FermionOperator()
15.
16. if get_ccsd_n_term(qn, en) != len(para):
17. assert False
18.
19. cnt = 0
20. fermion_op = FermionOperator()
21. for i in range(en):
22. for ex in range(en, qn):
23. fermion_op += FermionOperator(str(ex) + "+ " + str(i), para[cnt])
24. cnt += 1
25.
26. for i in range(n_electron):
27. for j in range(i+1,n_electron):
28. for ex1 in range(n_electron,n_qubit):
29. for ex2 in range(ex1+1,n_qubit):
30. fermion_op += FermionOperator(
31. str(ex2)+"+ "+str(ex1)+"+ "+str(j)+" "+str(i),
32. para[cnt]
33. )
34. cnt += 1
35.
36. return fermion_op
JordanWignerTransform接口的作用是将费米子哈密顿量转换成泡利哈密顿量。
1.def JordanWignerTransform(fermion_op):
2. data = fermion_op.data()
3. pauli = PauliOperator()
4. for i in data:
5. pauli += get_fermion_jordan_wigner(i[0][0])*i[1]
6. return pauli
get_fermion_jordan_wigner接口则是将费米子哈密顿量的子项转换成泡利哈密顿量。
1.def get_fermion_jordan_wigner(fermion_item):
2. pauli = PauliOperator("", 1)
3.
4. for i in fermion_item:
5. op_qubit = i[0]
6. op_str = ""
7. for j in range(op_qubit):
8. op_str += "Z" + str(j) + " "
9.
10. op_str1 = op_str + "X" + str(op_qubit)
11. op_str2 = op_str + "Y" + str(op_qubit)
12.
13. pauli_map = {}
14. pauli_map[op_str1] = 0.5
15.
16. if i[1]:
17. pauli_map[op_str2] = -0.5j
18. else:
19. pauli_map[op_str2] = 0.5j
20.
21. pauli *= PauliOperator(pauli_map)
22.
23. return pauli
cc_to_ucc_hamiltonian接口的作用是CC模型对应的哈密顿量转成UCC模型对应的哈密顿量。
1.def cc_to_ucc_hamiltonian(cc_op):
2. '''
3. generate Hamiltonian form of unitary coupled cluster
4. based on coupled cluster,H=1j*(T-dagger(T)),
5. then exp(-iHt)=exp(T-dagger(T))
6. '''
7. return 1j*(cc_op-cc_op.dagger())
get_expectation接口,作用是计算体系哈密顿量在试验态下的期望,接收的参数是轨道个数,电子个数,UCC模型,体系哈密顿量的一个子项。
1.def get_expectation(n_qubit, n_en, ucc,component):
2. '''
3. get expectation of one hamiltonian.
4. n_qubit: qubit number
5. n_en: electron number
6. ucc: unitary coupled cluster operator
7. component: paolioperator and coefficient,e.g. ('X0 Y1 Z2',0.2)
8. '''
9.
10. machine=init_quantum_machine(QMachineType.CPU)
11. q = machine.qAlloc_many(n_qubit)
12. prog=QProg()
13.
14. prog.insert(prepareInitialState(q, n_en))
15. prog.insert(simulate_hamiltonian(q, ucc, 1.0, 4))
16.
17. for i, j in component[0].items():
18. if j=='X':
19. prog.insert(H(q[i]))
20. elif j=='Y':
21. prog.insert(RX(q[i],pi/2))
22.
23. machine.directly_run(prog)
24. result=machine.get_prob_dict(q, select_max=-1)
25. machine.qFree_all(q)
26.
27. expectation=0
28. #奇负偶正
29. for i in result:
30. if parity_check(i, component[0]):
31. expectation-=result[i]
32. else:
33. expectation+=result[i]
34. return expectation*component[1]
prepareInitialState接口的作用是制备初态,接收的参数是一组量子比特和电子个数。
1.def prepareInitialState(qlist, en):
2. '''
3. prepare initial state.
4. qlist: qubit list
5. en: electron number
6. return a QCircuit
7. '''
8. circuit = QCircuit()
9. if len(qlist) < en:
10. return circuit
11.
12. for i in range(en):
13. circuit.insert(X(qlist[i]))
14.
15. return circuit;
simulate_hamiltonian接口,作用是构造哈密顿量的模拟线路,接收的参数是一组量子比特,泡利哈密顿量、演化时间演化次数。
1.def simulate_hamiltonian(qubit_list,pauli,t,slices=3):
2. '''
3. Simulate a general case of hamiltonian by Trotter-Suzuki
4. approximation. U=exp(-iHt)=(exp(-i H1 t/n)*exp(-i H2 t/n))^n
5. '''
6. circuit =QCircuit()
7.
8. for i in range(slices):
9. for op in pauli.data():
10. term = op[0][0]
11. circuit.insert(
12. simulate_one_term(
13. qubit_list,
14. term, op[1].real,
15. t/slices
16. )
17. )
18.
19. return circuit
simulate_one_term是构造哈密顿量子项的模拟线路。
1.def simulate_one_term(qubit_list, hamiltonian_term, coef, t):
2. '''
3. Simulate a single term of Hamilonian like "X0 Y1 Z2" with
4. coefficient and time. U=exp(-it*coef*H)
5. '''
6. circuit =QCircuit()
7.
8. if not hamiltonian_term:
9. return circuit
10.
11. transform=QCircuit()
12. tmp_qlist = []
13. for q, term in hamiltonian_term.items():
14. if term is 'X':
15. transform.insert(H(qubit_list[q]))
16. elif term is 'Y':
17. transform.insert(RX(qubit_list[q],pi/2))
18.
19. tmp_qlist.append(qubit_list[q])
20.
21. circuit.insert(transform)
22.
23. size = len(tmp_qlist)
24. if size == 1:
25. circuit.insert(RZ(tmp_qlist[0], 2*coef*t))
26. elif size > 1:
27. for i in range(size - 1):
28. circuit.insert(CNOT(tmp_qlist[i], tmp_qlist[size - 1]))
29. circuit.insert(RZ(tmp_qlist[size-1], 2*coef*t))
30. for i in range(size - 1):
31. circuit.insert(CNOT(tmp_qlist[i], tmp_qlist[size - 1]))
32.
33. circuit.insert(transform.dagger())
34.
35. return circuit
paity_check是对量子态中指定比特1的个数做奇偶校验。
1.def parity_check(number, terms):
2. '''
3. pairty check
4. number: quantum state
5. terms: a single term of PauliOperator, like"[(0, X), (1, Y)]"
6. '''
7. check=0
8. number=number[::-1]
9. for i in terms:
10. if number[i]=='1':
11. check+=1
12. return check%2
optimize_by_no_gradient是非梯度下降优化算法的主体接口,需要传入的一组参数是体系哈密顿量,轨道个数,电子个数,优化器迭代次数
接口的具体实现步骤是:首先初始化一组待优化的参数,然后构造一个非梯度下降优化器,这里构造的优化器是Nelder-Mead,设置优化器的迭代次数并向优化器注册计算期望的损失函数,然后执行优化器,最后返回优化器优化的最低期望值。
1.def optimize_by_no_gradient(mol_pauli, n_qubit, n_en, iters):
2. n_para = get_ccsd_n_term(n_qubit, n_electron)
3.
4. para_vec = []
5. for i in range(n_para):
6. para_vec.append(0.5)
7.
8. no_gd_optimizer = OptimizerFactory.makeOptimizer(OptimizerType.NELDER_MEAD)
9. no_gd_optimizer.setMaxIter(iters)
10. no_gd_optimizer.setMaxFCalls(iters)
11. no_gd_optimizer.registerFunc(partial(
12. loss_func,
13. qubit_number = n_qubit,
14. electron_number = n_en,
15. Hamiltonian=mol_pauli.toHamiltonian(1)),
16. para_vec)
17.
18. no_gd_optimizer.exec()
19. result = no_gd_optimizer.getResult()
20. print(result.fun_val)
21.
22. return result.fun_val
etAtomElectronNum接口作用是返回原子对应的电子个数。
1.def getAtomElectronNum(atom):
2. atom_electron_map = {
3. 'H':1, 'He':2, 'Li':3, 'Be':4, 'B':5, 'C':6, 'N':7, 'O':8, 'F':9, 'Ne':10,
4. 'Na':11, 'Mg':12, 'Al':13, 'Si':14, 'P':15, 'S':16, 'Cl':17, 'Ar':18
5. }
6.
7. if (not atom_electron_map.__contains__(atom)):
8. return 0
9.
10. return atom_electron_map[atom]
该算法演示示例对应的主函数,首先构造一组不同距离下的氢分子模型,然后计算每个氢分子模型对应的基态能量,最后将计算的结果绘制成曲线图。
1.if __name__=="__main__":
2. distances = [x * 0.1 for x in range(2, 25)]
3. molecule = "H 0 0 0\nH 0 0 {0}"
4.
5. molecules = []
6. for d in distances:
7. molecules.append(molecule.format(d))
8.
9. chemistry_dict = {
10. "mol":"",
11. "multiplicity":1,
12. "charge":0,
13. "basis":"sto-3g",
14. }
15.
16. energies = []
17.
18. for d in distances:
19. mol = molecule.format(d)
20.
21. chemistry_dict["mol"] = molecule.format(d)
22. data = run_psi4(chemistry_dict)
23. #get molecule electron number
24. n_electron = 0
25. mol_splits = mol.split()
26. cnt = 0
27. while (cnt < len(mol_splits)):
28. n_electron += getAtomElectronNum(mol_splits[cnt])
29. cnt += 4
30.
31. fermion_op = parsePsi4DataToFermion(data[1])
32. pauli_op = JordanWignerTransform(fermion_op)
33.
34. n_qubit = pauli_op.getMaxIndex()
35.
36. energies.append(optimize_by_no_gradient(pauli_op, n_qubit, n_electron, 200))
37.
38. plt.plot(distances , energies, 'r')
39. plt.xlabel('distance')
40. plt.ylabel('energy')
41. plt.title('VQE PLOT')
42. plt.show()
该示例对应的输出结果如下,曲线图是氢分子在不同距离下对应的基态能量:
图附3.3.2 氢分子在不同距离下对应的基态能量