《量子计算编程实战》读书笔记(一)

前言

《量子计算编程实战》读书笔记(一)

抱歉!有的读者就想问了,新系列的开头本该是喜喜庆庆的,干哈上来就抱个歉乎?原因无他,就是由于上一坑还没填完,如今又掘了一坑,容易给读者留下随随便便而又不负责任的印象。鉴于此,正好赶上放了“寒假”,可又囿于高三学业,注定不能像年轻时(指一两年前)那样高效输出了。为此便郑重抱歉……想起班主任的话:虽不一定尽善尽美,但一定全力以赴。送给亲爱的你们,也送给我自己。

至于为什么选了《量子计算编程实战——基于IBM QX量子计算平台》这一本书,浏览过前一个系列的读者肯定知道,我本身是走Qiskit量子开发的,Q#虽然也用过但给我的印象不太好,更何况IBM自己也有货真价实(价格不价格先放一边)的量子计算机,从库本身和官方提供的平台来看,还是选择了Qiskit。一开始笔者和群友兼战友们都是看的英文文档入门(当然我一方面由于学业原因,一方面还是个半瓶子晃荡的主,也没深入多少),之前就有过将其翻译成中文文档以在国内普及的想法,后来无意间网购看到了这本书,内心甚是惊喜,因为这本身就代表着一个小趋势,未来发展的一个小方向,更何况是清华大学出版社出版,就更加说明了国内是有相应能力的。可惜,买了很久都未曾打开翻阅过,直到上星期请假回家看了一会儿才发现其中奥妙。近来有些网友向我反映很多我未曾见过的问题,可能是由于我深入的不多,但我更怀疑是官方更新迭代得太快,很多教程已经失去时效性和实用性,读起来无异嚼甘蔗——汲取到的并不多,但当我读了这本书的第二三章后,猛地发现,作者明摆着是要让读者亲手建立一个基础的量子计算体系,这一部分既有利于理解量子计算,又独立于Qiskit本身,实在是幸甚至哉,妙不可言!

多少有些功利性的色彩,我会略去第一章有关历史、比较、评价的内容,直接从第二章起笔。愿同诸君共赏!

环境:Windows 11 + Visual Studio 2022 + Conda 4.9.0 + Qiskit 0.34.1

第2章 量子比特

经典比特因其0和1的二元性常常被类比为开关、黑白等,而从这个角度,量子比特则被类比为介于0和1间的光谱(spectrum)。光谱一词意蕴很丰富:既点出了量子比特之状态的可连续性特点(切忌非黑即白思维),又扼要地说明了“叠加”的意义——你可以通过0和1两个基本态,或说基矢调制一系列状态!

《量子计算编程实战》读书笔记(一)

 一个量子比特得益于其众多的表示形式,从而可以节省时间和空间。

关于|0>和|1>

可能很多读者不大理解标题里的狄拉克矢(Dirac Vector)的表示方式,其实一旦立足于量子力学“数理哲”不分家的思想就很好理解:每一个这样的矢量或说状态代表着你的立场或说看问题的角度 

《量子计算编程实战》读书笔记(一)

上述基矢来自于汉语言文学,但是我们量子计算终归还是要回到数学的,0和1两种基态被数学地表示为二维矢量:

import numpy as np
zero_qubit = np.matrix('1; 0')    #具体取名全看个人意愿,qubit也可以写成qb,
one_qubit = np.matrix('0; 1')     #实在极简还可以分别表示成qb0和qb1,笔者这
                                  #里需要和原书保持一致

甚至"0"和"1"本身都不一定要叫这个名字,你可以管他们一个叫小狗,一个叫小猫,王二张三什么的,但是只有"0"和"1"才被国际认可,也被国际通用……关于二者的组合,学名是“叠加”(superposition),实际上和线性代数中的线性组合一致,不熟悉高等代数的同学可以联系一下文章开头的光谱:一旦你有了两种颜色,你可以任意调制二者比例获得介于二者间的任意新的颜色。定义新函数:

## 按照零矢和壹矢的相对比例组合量子态
def zero_to_one(precentage_zero : float, precentage_one: float):
    if not precentage_zero + precentage_one == 100:
        raise Exception('the sum of the square of precentages must be equal to 100 and both precentage must be positive.')
    return np.sqrt(precentage_zero/100.)*zero_qubit + np.sqrt(precentage_one/100.)*one_qubit

注意,在量子力学中有一个规定,即你选择基矢的相对比例的平方和必须为一!(即《量子计算编程实战》读书笔记(一)),事实上,相对比例的平方代表着你测量结果的概率,概率之和为1是自然的。有些读者可能还是读来倍感云里雾里,实则可以考虑高尔顿板:

《量子计算编程实战》读书笔记(一)
高尔顿钉板

把你的基矢们(我可没说过基矢只有两个呦,实际上如果线性无关,也就是相互独立,不能表示的话,可以是任意个基矢)看成是高尔顿钉板底部的诸多竖直槽,落下去的小球相对比例就代表了进入该槽的概率(频数代替个数)。

那么现在让我们开始组合创造新的量子态:

qb_50_50 = zero_to_one_qubit(50, 50)
qb_10_90 = zero_to_one_qubit(10, 90)

《量子计算编程实战》读书笔记(一)   《量子计算编程实战》读书笔记(一)

(系数平方和为1是显而易见的)

量子比特的三种表示形式

原书摘录:

“……对于某些选择来说,在特定基态上编写算法更容易。此外,每种基态的物理原理都不同,因此在特定基态上描述物理系统可能会更方便。最后要注意的是,通过一些代码或数学运算,可以从一个基态切换到另一个基态,这在计算中可能很有用,就像从另一个角度看同一个物体可能有助于判断它的形状一样……”

量子比特的三种表示形式分别对应空间直角坐标系(如果你这样看问题的话)的三个轴上的正交基矢:《量子计算编程实战》读书笔记(一).我们已然见过的0和1是z轴上一对相反的单位基矢,另两对分别位于x和y轴:

《量子计算编程实战》读书笔记(一)

《量子计算编程实战》读书笔记(一)

注:原书后一对本来是指顺时针和逆时针,csdn里的latex打不出来,我就用s代表顺,f代表反了,但这样肯定会有人打我……;i是虚数单位《量子计算编程实战》读书笔记(一)

思考:为什么分母是《量子计算编程实战》读书笔记(一)

代码中定义如下:

# 正负基态 x轴
plus_qubit = 1 / np.sqrt(2) * np.matrix('1; 1')
minus_qubit = 1 / np.sqrt(2) * np.matrix('1; -1')

# 顺逆基态 y轴
clockwise_qubit = 1/np.sqrt(2) * np.matrix([[1], [np.complex(0, 1)]])
counterclockwise_qubit = 1/np.sqrt(2) * np.matrix([[1], [-np.complex(0, 1)]])

目前来看,这三对基矢还是比较抽象,所以聪明的量子计算专家发明了一种图——布洛赫球面(Bloch Sphere)

 布洛赫球面

有过先前阅读经历的读者肯定知道,Qiskit绘图库中有相应函数,但是本书则另辟蹊径,手把手教读者写一个绘图函数!

原理很简单,先前提到过,你选择基矢的相对比例的平方和必须为一!(即《量子计算编程实战》读书笔记(一)),当我们只考虑这三对基矢时,从几何的角度不难联想到《量子计算编程实战》读书笔记(一),该方程实际上在空间直角坐标系中代表着一个单位球面,故而我们也就可以通过某种方式直观地表示单个量子比特(抽象→具体)!

《量子计算编程实战》读书笔记(一)

 代码实现(相同代码逻辑重构了三遍而已,留心坐标的计算过程):

数学上,对于复数的平方我们要用某一复数《量子计算编程实战》读书笔记(一)乘以其共轭(conjugated)复数《量子计算编程实战》读书笔记(一)

def get_bloch_coordinates(qubit):
    def get_x_bloch(qubit):
        qubit_x_basis = 1./np.sqrt(2) * np.matrix('1 1; 1 -1') * qubit
        prob_zero_qubit = (qubit_x_basis.item(0) * qubit_x_basis.item(0).conjugate()).real
        prob_one_qubit = (qubit_x_basis.item(1) * qubit_x_basis.item(1).conjugate()).real
        return prob_zero_qubit - prob_one_qubit
    def get_y_bloch(qubit):
        qubit_y_basis = 1./np.sqrt(2) * np.matrix('1 1; 1 -1') * np.matrix([[1,0], [0, -np.complex(0,1)]]) * qubit
        prob_zero_qubit = (qubit_y_basis.item(0) * qubit_y_basis.item(0).conjugate()).real
        prob_one_qubit = (qubit_y_basis.item(1) * qubit_y_basis.item(1).conjugate()).real
        return prob_zero_qubit - prob_one_qubit
    def get_z_bloch(qubit):
        qubit_z_basis = qubit
        prob_zero_qubit = (qubit_z_basis.item(0) * qubit_z_basis.item(0).conjugate()).real
        prob_one_qubit = (qubit_z_basis.item(1) * qubit_z_basis.item(1).conjugate()).real
        return prob_zero_qubit - prob_one_qubit
    return (get_x_bloch(qubit), get_y_bloch(qubit), get_z_bloch(qubit))

 绘制坐标:

def plot_bloch(qubit, color='b', ax = None):
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    if not ax:
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        # 绘制球体
        u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
        x = np.cos(u)*np.sin(v)
        y = np.sin(u)*np.sin(v)
        z = np.cos(v)
        ax.plot_wireframe(x, y, z, color="k", alpha=.1)
        ax.grid(0)

    coordinates = get_bloch_coordinates(qubit)
    ax.quiver([0],[0],[0],[coordinates[0]], [coordinates[1]], [coordinates[2]],length=1,color=color,arrow_length_ratio=0.3)
    ax.set_xlim([-1,1])
    ax.set_ylim([-1,1])
    ax.set_zlim([-1,1])
    ax.view_init(azim=20)
    # savefig('pic.jpg')
    return ax

注:具体实现可以不细看!对于Jupyter用户到这里可以结束了,但对于Visual Studio (Code)用户还要在函数里指定图片保存地址(根据个人情况修改注释掉的那一行);另,此处会调用即可。

测量量子态

聪明的读者已然可以通过通读全文大致猜出如何测量量子态了,答案就藏在第三张图里(这一部分是量子力学的内容,原书并未收录):

《量子计算编程实战》读书笔记(一)

原理很简单:

《量子计算编程实战》读书笔记(一)

物理上,假如我用一个方形线框去套一个方块100%可以套进去,但如果是去套比它大许多的球时有0%的概率会套进去。故而我们看到,测量my_state有10%的概率得到零矢!

有一点需要注意,那就是一旦选定基矢集,测量的结果就跑不出该集合,打个比方,假使我以书名、出版社、作者为基矢集,那么结果就不能是煎饼卷大葱,只能是三者中的一个!

当然,由于我们在编写布洛赫球面绘制函数的过程中已经包装了上述求解过程,自然可以通过布洛赫球面轻易读出任一量子态的可能结果及概率。

习题

《量子计算编程实战》读书笔记(一)

下一章:量子态、量子寄存器和测量

欢迎加入Qiskit交流群:1064371332

上一篇:Eclipse配置Tomcat服务器


下一篇:[易学易懂系列|rustlang语言|零基础|快速入门|(5)|生命周期Lifetime]