近几年,量子计算不仅在量子硬件方面有所发展,在量子算法的开发方面也迎来了改进。随着 NISQ(Noisy Intermediate Scale Quantum) 计算机的推出,开发用于探索这些机器强大能力的算法越来越重要。然而,在 NISQ 处理器上设计量子算法时的一个常见问题是如何充分利用这些有限的量子器件。此外,一些量子处理器具有复杂的几何约束和其他细微差别,忽略这些因素将导致错误的量子计算,或影响优化和修改的计算结果。
量子计算机能够高效运行 Shor 因子分解算法,能够解决对于经典计算机来说不可行的因子分解问题,但我们需要数量非常庞大的量子位——可能需要数百万个。虽然代价巨大,但这项开销却是必须的,但目前大多数量子算法对噪声的影响都非常敏感。
(一)含噪中型量子
简介
NISQ 是含噪中型量子(Noisy Intermediate-Scale Quantum) 的简称。这个概念由美国理论物理学家 John Preskill 提出。
噪声(Noisy) 指的是现阶段由于对量子比特未能完全控制所产生的不准确性,正是这种不准确性在短期内严重制约了量子设备实现其目标;“中型” 指的是在接下来的几年中,量子计算机将要实现的量子规模大小,届时,量子比特的数目将可能从 50 到几百个不等。拥有 50-100 量子比特、以及高保真量子门 (Quantum Gate) 的计算机,便可称为 NISQ 计算机,凭借量子纠缠概念中的量子复杂性和量子纠错这两个原则,NISQ 计算机能够执行超越经典的数字计算机的任务。
量子计算机的优势与掣肘
我们知道依托于量子态叠加和量子纠缠,量子计算机可以发挥出比经典计算机更强大的性能来。而至少有三点理由是可以充分的说明这个说法的:
1.经典难解问题的量子算法。针对目前对于经典计算机来说很难的某些问题,已经发明了量子算法,可以很容易地解决这些问题。最著名的例子就是如何寻找一个很大的整数复合它素因子的问题。通常我们认为因式分解很难,因为许多聪明人几十年来一直试图找到更好的因式分解算法,但都没有成功。凭借量子计算机强大的算力,量子计算机在这方面取得了很大的成功。
2.复杂性理论的论证。理论计算机的研究者们基于复杂性理论提供了论据,表明(在合理的假设下)容易用量子计算机制备具有超经典特性的量子态。具体而言,如果我们测量所有处于这种状态下的量子比特,我们就是从一个相关的概率分布中取样,而这个概率分布是无法用任何有效的经典手段取样的,但是量子计算机可以做到。
3.没有任何已知的经典算法可以模拟量子计算机。虽然,我们认为量子计算强大的论据,有可能仅仅是因为我们不知道如何用经典计算机来模拟量子计算机,但是物理学家为了寻找更好的模拟量子计算机的方法也已经经过了几十年,仍未有收获。或许,这也能侧面佐证这一观点是正确的了。
明白了量子计算机为什么具有优势的理由后,有必要了解一下为什么现如今物理形态的量子计算机仍然处于实验室与研究的阶段的原因。
量子计算机凭借量子特性获得巨大算力,同样也因为量子特性产生了使用问题。目前我们无法在不对体系产生不可控干扰的情况下观察一个量子系统。这意味着,如果我们想用一个量子系统来存储和可靠地处理信息,那么我们需要让这个系统与外界完全隔离。但与此同时,我们希望量子比特间有强烈的相互作用,以便我们能够处理信息;我们还需要能够从外部控制该系体,并最终读出量子比特,以便我们能够找到计算的结果。
最终的结果就是,虽然我们能够利用量子纠错的原理来保护量子系统不受外界干扰(通过将量子系统编码在一个非常高度纠缠的状态中),但因为要确认计算结果,对量子系统的干扰还是不可避免地产生了。为了利用量子纠错的原理,我们在量子比特数上还有非常大的开销,但目前我们能够掌握的量子比特数还不足以提供非常可靠的容错数,所以不可避免的受到噪声干扰,这也是 NISQ 概念提出的背景。
当程序在超过 50 个量子位元的装置上执行时,工作量就会庞大到难以在传统电脑上进行模拟了,此时我们需要量子计算机来完成这些传统电脑无法完成的工作。但是因为目前的量子技术无法提供对于算法来说足够的容错能力,导致必须在物理层面上使用不完美的量子位元。相关的研究工作者们一直在致力于解决如何在不完美的 NISQ 处理器上开发量子演算的算法,得到正确的计算结果。
Criq 算法框架就是谷歌为了改善精确度和降低量子计算中的噪声而设计的框架,这是专为 NISQ 算法打造的框架。
(二)Cirq 简介
Cirq 是一个 Python 框架,由 Google Quantum AI 团队发布,用于编写、操作和优化量子电路,然后在量子计算机和量子模拟器上运行它们。Cirq 是主要用于含噪中型量子的计算机开源框架,为处理当今含噪声的中等规模量子计算机提供了有意义的思路。
Cirq 算法框架能够让研究人员为特定的量子处理器编写量子算法,用户可以对量子电路的精确控制,将量子门适当地放置于设备上,并在量子硬件的约束范围内对这些量子门进行调度。为了编写和编译量子电路,Cirq 的数据结构是专门优化过的,能够让开发者更加充分地利用 NISQ 架构。另外,Cirq 支持在本地模拟器上运行这些算法,并可以轻松地与量子硬件或更大的规模的云端模拟器集成起来,为实验成果提供了很好的延续性。
Cirq 算法框架是在 Apache2 协议下开源的,可以自由修改或嵌入到任何商业或开源软件包中。我们可以通过官方网站安装它:
1.安装 cirq:pip
python -m pip install --upgrade pippython -m pip install cirq
复制代码
2.在 cirq.contrib 中安装功能的依赖项:
python -m pip install cirq-core[contrib]
复制代码
3.运行一个简单的示例检查程序是否安装成功:
import cirq
# Pick a qubit.qubit = cirq.GridQubit(0, 0)
# Create a circuitcircuit = cirq.Circuit( cirq.X(qubit)**0.5, # Square root of NOT. cirq.measure(qubit, key='m') # Measurement.)print("Circuit:")print(circuit)
# Simulate the circuit several times.simulator = cirq.Simulator()result = simulator.run(circuit, repetitions=20)print("Results:")print(result)
复制代码
输出示例:
Circuit:(0, 0): ───X^0.5───M('m')───Results:m=11000111111011001000
复制代码
Google Quantum AI 团队还发布了 OpenFermion-Cirq 项目,这是一个基于 Cirq 的应用程序示例。OpenFermion 是一个为化学问题开发的量子计算平台,而 OpenFermion-Cirq 则是一个开源库,可将量子模拟算法编译成 Cirq。比如,它可以完成搭建量子变分算法,模拟分子或者复杂材料的性质等等。
(三)Cirq 实例
成功安装之后,我们可以通过一些示例来看看,Cirq 究竟可以完成什么样的任务。
1.构建电路
Cirq 中量子程序的主要表示方法是类,A 是所有在同一抽象时间片段中的行为的集合,An 是对量子比特的特定子集起作用的某种效应,最常见的类型是 .CircuitCircuitMomentsMomentOperationsOperationOperationGateOperation
这个结构的基础就是量子位的概念。在 Cirq 中,量子位和其他量子对象是由 Cirq.Qid 基类的子类实例来识别的,不同子类的 Qid 可以用于不同的情况。例如,谷歌的设备多使用的量子比特同城被安排在一个正方形网格的顶点上。为此,可以通过 cirq.GridQubit 类将 cirq.Qid 子类化,比如我们可以用下面的方法创建一个 3 乘 3 的量子比特网格:
qubits = [cirq.GridQubit(x, y) for x in range(3) for y in range(3)]
print(qubits[0])
复制代码
输出:
下一个层级是 cirq.Gate 的概念。一个 cirq.Gate 代表了发生在一个量子比特上的物理过程,门的一个非常重要的属性是它可以被应用于一个或多个量子比特,可以通过下面所示的方法完成:
# This is an Pauli X gate. It is an object instance.x_gate = cirq.X# Applying it to the qubit at location (0, 0) (defined above)# turns it into an operation.x_op = x_gate(qubits[0])
print(x_op)
复制代码
输出:
cirq.Moment 只是一个操作的集合,每个操作都是在不同的量子比特集合上进行的,在概念上表示这些操作发生在这个抽象的时间片段上。这个结构本身并不需要与量子计算机上实际操作的调度有关,也可以不通过模拟器运行。比如,利用 Moment Moment X CZ 在三个量子比特的基础上构造矩阵:
cz = cirq.CZ(qubits[0], qubits[1])x = cirq.X(qubits[2])moment = cirq.Moment(x, cz)
print(moment)
复制代码
结果为:
╷ 0 1 2╶─┼───────0 │ @─@ X │
复制代码
以上展示的方法不是构造矩阵的唯一方法,但它能够说明 A 只是对不相交的量子比特集所作操作的集合。
如果要利用 Cirq 来构建电路,我们可以通过很多种方法来构建,比如将 Circuit.append 添加到电路上:
q0, q1, q2 = [cirq.GridQubit(i, 0) for i in range(3)]circuit = cirq.Circuit()circuit.append([cirq.CZ(q0, q1), cirq.H(q2)])
print(circuit)
复制代码
输出结果为:
(0, 0): ───@─── │(1, 0): ───@───
(2, 0): ───H───
复制代码
除此之外,我们还可以添加新的 Moment Circuit 在电路中:
circuit.append([cirq.H(q0), cirq.CZ(q1, q2)])print(circuit)
复制代码
结果为:
(0, 0): ───@───H─── │(1, 0): ───@───@─── │(2, 0): ───H───@───H───
复制代码
当然为了实现更多进阶操作,Cirq 框架在建立电路方面还有很多其他的功能提供,比如对电路进行切片和迭代、嵌套电路和电路操作等等。这里我们将展示一下如何对电路进行切片和迭代的操作。
当对电路进行迭代时,迭代中的每个项都是一个 moment :
circuit = cirq.Circuit(cirq.H(q0), cirq.CZ(q0, q1))for moment in circuit: print(moment)
复制代码
输出结果为:
╷ 0╶─┼───0 │ H │ ╷ 0╶─┼───0 │ @ │ │1 │ @ │
复制代码
同时,切片 A 会产生一个新的、只与切片对应的矩阵 Circuit Circuit
circuit = cirq.Circuit(cirq.H(q0), cirq.CZ(q0, q1), cirq.H(q1), cirq.CZ(q0, q1))
print(circuit[1:3])
复制代码
(0, 0): ───@─────── │(1, 0): ───@───H───
复制代码
这两种方法有两种非常有用的应用,一种是去掉最后的 moment(通常只是测量值)还有一种就是反转电路 circuit[:-1]、circuit[::-1]。
2.模拟电路
Cirq 可以通过使用经典计算机模拟量子计算,来计算量子电路的影响。Cirq 除了能够模拟一个像数学理论所期望那样的完美无噪声的量子计算机,还可以模拟更逼真的、受误差和噪声影响的量子计算机,并且还能够将仿真结果可视化,制作出基态直方图。
在进行模拟操作时,我们知道拥有实现噪声量子演化的物体是很有用的,Cirq 支持通过噪声的算子和来表示对噪声进行建模,并可以通过 DensityMatrixSimulator 模块模拟任何含噪的量子电路,因为它存储了整个密度矩阵ρ,这种模拟策略通过直接作用于每个量子信道的克劳斯算子来更新状态,具体操作如下:
"""Simulating a circuit with the density matrix simulator."""# Get a circuit.qbit = cirq.GridQubit(0, 0)circuit = cirq.Circuit( cirq.X(qbit), cirq.amplitude_damp(0.1).on(qbit))# Display it.print("Simulating circuit:")print(circuit)
# Simulate with the density matrix simulator.dsim = cirq.DensityMatrixSimulator()rho = dsim.simulate(circuit).final_density_matrix
# Display the final density matrix.print("\nFinal density matrix:")print(rho)
复制代码
输出:
Simulating circuit:(0, 0): ───X───AD(0.1)───
Final density matrix:[[0.1 +0.j 0. +0.j] [0. +0.j 0.90000004+0.j]]
复制代码
在实际使用过程中,为了满足更多的使用场景及模拟需求,Cirq 在模拟电路时还支持在电路中自由的增加噪声。通常电路仅使用酉操作来定义,但在 Cirq 中,我们可以给每个电路插入一个噪声通道,具体代码操作如下:
"""One method to insert noise in a circuit."""# Define some noiseless circuit.circuit = cirq.testing.random_circuit( qubits=3, n_moments=3, op_density=1, random_state=11)
# Display the noiseless circuit.print("Circuit without noise:")print(circuit)
# Add noise to the circuit.noisy = circuit.with_noise(cirq.depolarize(p=0.01))
# Display it.print("\nCircuit with noise:")print(noisy)
复制代码
输出结果:
Circuit without noise: ┌──┐0: ───@───X─────×──── │ │ │1: ───@───┼────S┼──── │ │2: ───Z───@─────×──── └──┘
Circuit with noise: ┌──┐0: ───@───D(0.01)[cirq.VirtualTag()]───X───D(0.01)[cirq.VirtualTag()]─────×────D(0.01)[cirq.VirtualTag()]─── │ │ │1: ───@───D(0.01)[cirq.VirtualTag()]───┼───D(0.01)[cirq.VirtualTag()]────S┼────D(0.01)[cirq.VirtualTag()]─── │ │2: ───Z───D(0.01)[cirq.VirtualTag()]───@───D(0.01)[cirq.VirtualTag()]─────×────D(0.01)[cirq.VirtualTag()]─── └──┘
复制代码
在完成对电路的自定义操作后,再输出结果时,我们可以选择使用 Cirq 输出直方图,更直观的去展示模拟结果,具体操作方法如下:
q = cirq.LineQubit.range(4)circuit = cirq.Circuit([cirq.H.on_each(*q), cirq.measure(*q)])result = cirq.Simulator().run(circuit, repetitions=100)_ = cirq.plot_state_histogram(result, plt.subplot())plt.show()
复制代码
当遇到某些数据结果较为稀疏的电路图时,还可以通过仅绘制直方图中的非零条目来时直方图更易阅读:
q = cirq.LineQubit.range(6)circuit = cirq.Circuit([cirq.H(q[0]), [cirq.CNOT(q[0], q[i]) for i in range(1, 6, 1)], cirq.measure(*q, key='measure_all')])result = cirq.Simulator().run(circuit, repetitions=100)_ = cirq.plot_state_histogram(result, plt.subplot())
histogram = result.histogram(key = 'measure_all')_ = cirq.plot_state_histogram(histogram, plt.subplot())plt.show()
复制代码
这样编辑之后输出的直方图结果为:
3.转换电路
为了保证量子电路能够在量子硬件设备上运行,或者提高电路在设备上的可靠性,Cirq 提供了几种转换电路的方法。
Cirq 中的转换器是可以随时被调用的,它满足 cirq.TRANSFORMER 这一 API,并且它能够将一个输入电路转换成一个输出电路。为了将用户自定义的电路编译成可以在特定设备或模拟器上执行的等效电路,通常需要进行电路转换,编译过程通常包括以下步骤:
门分解:只是用属于设备目标门集的门重写电路,及设备可以执行的门集。
量子位映射和路由:将输入电路中的逻辑量子位映射到设备上的物理量子位,并插入适当的交换操作,使最终电路着重于硬件拓扑。
电路优化:执行具体的硬件优化,比如使用更有效地重写来合并和替换连接 1 和 2 操作的组件,通过电路换算 Z 门,调整门的瞬间等等。
Cirq 提供了许多开箱即用的转换器,它们可以用作单独的编译通道,并且还提供了一个通用框架,让用户使用强大的原语创建自己的转换器,并将一堆转换器捆绑在一起,以便为特定目标编译电路。
Cirq 内置转换器的功能丰富多样,如支持免编译标签、支持递归变换子电路、编译目标门集等等。这里我们简单介绍一下 Cirq 是如何将电路编译为 NISQ 目标的。通常 Cirq 只需要在输入电路上一个接一个的运行少量单独的编译传递,就能够将电路编译为 NISQ 目标。
这些操作的前置条件是要对转换器进行预处理,预处理包括优化,例如将 1/2 量子位酉的连接分量合并到单个酉矩阵中,然后将其替换为有效的分析分解;完成预处理后,进入到转换器的后处理步骤,一般包括清理和优化,例如丢弃可忽略不计的操作,将单个量子位旋转换为所需的形式,电路对准等等。代码如下:
# Original QFT Circuit on 3 qubits.q = cirq.LineQubit.range(3)circuit = cirq.Circuit(cirq.QuantumFourierTransformGate(3).on(*q), cirq.measure(*q))print("Original Circuit:", circuit, "\n", sep="\n")
# Compile the circuit for CZ Target Gateset.gateset = cirq.CZTargetGateset(allow_partial_czs=True)cz_circuit = cirq.optimize_for_target_gateset(circuit, gateset = gateset)cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(circuit, cz_circuit)print("Circuit compiled for CZ Target Gateset:", cz_circuit, "\n", sep="\n")
复制代码
输出结果为:
Original Circuit:0: ───qft───M─── │ │1: ───#2────M─── │ │2: ───#3────M───
Circuit compiled for CZ Target Gateset:0: ───PhX(0.5)^0.5───@────────PhXZ(a=1.11e-16,x=1,z=0)───────────PhX(0.5)^0.5───@───PhX(-0.5)^0.5───@───PhX(0.5)^0.5────@────────PhXZ(a=0.5,x=0.5,z=-1.0)───M─── │ │ │ │ │1: ──────────────────@^-0.5───PhXZ(a=-1,x=0.5,z=-0.5)────@──────────────────────┼───────────────────┼───────────────────┼───────────────────────────────────M─── │ │ │ │ │2: ──────────────────────────────────────────────────────@^0.5───PhX(0.5)^0.5───@───PhX(0.5)^0.5────@───PhX(-0.5)^0.5───@^0.75───PhXZ(a=0.25,x=0,z=0.25)────M───
复制代码
4.管理噪声
在真正的量子设备上运行电路时,意味着我们同时需要处理这些设备引入计算后的噪声,以此来提高在硬件上运行时电路的可靠性。Cirq 可通过将噪声可视化的方式来进行更好的校准工作。
Cirq 绘制的噪声热图分为单量子位热图和双量子位热图两种,使用时我们可以从网格量子位元组到相应浮点值的自定义数据映射直接创建热图,操作代码如下:
single_qubit_heatmap = cirq.Heatmap({ (cirq.GridQubit(0, 0),): 0.1, (cirq.GridQubit(0, 1),): 0.2, (cirq.GridQubit(0, 2),): 0.3, (cirq.GridQubit(1, 0),): 0.4,})
_, ax = plt.subplots(figsize=(8, 8))_ = single_qubit_heatmap.plot(ax);
复制代码
最后的输出结果如下:
当然我们也可以利用类似的方式创建双量子位如图:
two_qubit_interaction_heatmap = cirq.TwoQubitInteractionHeatmap({ (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)): 0.1, (cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)): 0.2, (cirq.GridQubit(1, 0), cirq.GridQubit(0, 0)): 0.3, (cirq.GridQubit(3, 3), cirq.GridQubit(3, 2)): 0.9,}, annotation_format="0.2f", title = 'Example Two-Qubit Heatmap')
_, ax = plt.subplots(figsize=(8, 8))_ = two_qubit_interaction_heatmap.plot(ax)
复制代码
输出结果如下:
除了能够输出这两种基础的量子位热图外,我们还可以对输出的图像进行多种多样的编辑操作,满足更多形式展示需求,比如颜色栏属性、色彩映射表属性、批注文本属性等等。依据可视化后的校准指标,接下来让我们介绍一下如何校准电路中存在的噪声问题。
通常在 Cirq 中使用的校准方式主要分为两种:Floquet 和 XEB 校准。两种校准主要集中在减少相干误差的两个来源影响:校准漂移和串扰误差。这些误差在整个芯片上并不均匀,并且在量子位和器件校准之间的幅度上有所不同,在实现最佳性能比在期间上使用额外时间进行表征更重要的情况下,应使用电路特定的校准。
如果要运行校准,需要定义一个或多个电路,这里我们利用一个使用了单 门的简单电路进行示例:
"""Define your circuit(s) here."""qubits = cg.line_on_device(device_sampler.device, length=2)circuit = cirq.Circuit(cirq.ISWAP.on(*qubits) ** 0.5)
print("Example circuit:", circuit, sep="\n\n")
复制代码
此电路的输出结果为:
Example circuit:
(0, 5): ───iSwap─────── │(0, 6): ───iSwap^0.5───
复制代码
Floquet 校准
Floquet 校准的第一步是定义要表征的角度,它可以快速、准确地估计 cirq.PhasedFSimGate 的每个角度:
# Define calibration options.floquet_options = cg.FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=True, characterize_phi=True,)
复制代码
接下来使用这些选项来获取表征请求,然后表征它们:
# Get characterization requests.floquet_characterization_requests = cg.prepare_characterization_for_operations( circuit, options=floquet_options) # Characterize the requests on the engine.floquet_characterizations = cg.run_calibrations( floquet_characterization_requests, device_sampler.sampler)
复制代码
最后一步,是使用函数补偿偏移:
# Compensate your circuit based on the characterizations.floquet_calibrated_circuit = cg.make_zeta_chi_gamma_compensation_for_moments( circuit, floquet_characterizations).circuit
print("Floquet calibrated circuit:", floquet_calibrated_circuit, sep="\n\n")
复制代码
结果为:
Floquet calibrated circuit:
(0, 5): ───Rz(-0.459π)───FSim(0.25π, 0)───Rz(0.541π)──── │(0, 6): ───Rz(0.512π)────FSim(0.25π, 0)───Rz(-0.488π)───
复制代码
XEB 校准
XEB 校准的工作原理是在无噪声仿真器和噪声处理器上执行随机电路库,然后比较结果。原则上,它可以表征任何双量子位门,当然实际使用时也会产生一些限制。
与 Flouet 校准一样,使用 XEB 校准的第一步是定义选项。与 Flouet 校准不同的是,XEB 校准需要大量经典处理来表征角度,除了要表征的角度之外,我们还需要定义随机电路。
# Define calibration options.cycle_depths=(5, 25, 50, 100)
xeb_options = cg.LocalXEBPhasedFSimCalibrationOptions( cycle_depths=cycle_depths, n_processes=1, # Note that all angles below are set to True by default. fsim_options=cirq.experiments.XEBPhasedFSimCharacterizationOptions( characterize_theta=False, characterize_zeta=True, characterize_chi=True, characterize_gamma=True, characterize_phi=False, ),)
复制代码
其余步骤与 Flouet 校准相同:首先获得表征请求,然后在引擎上表征它们:
# Get characterization requests.xeb_characterization_requests = cg.prepare_characterization_for_operations( circuit, options=xeb_options)
# Characterize the requests on the engine.xeb_characterizations = cg.run_calibrations( xeb_characterization_requests, device_sampler.sampler)
复制代码
进行补偿也与 Floquet 校准示例相同:
xeb_calibrated_circuit = cg.make_zeta_chi_gamma_compensation_for_moments( circuit, xeb_characterizations,).circuit
print("XEB calibrated circuit:", xeb_calibrated_circuit, sep="\n\n")
复制代码
XEB calibrated circuit:
(0, 5): ───Rz(-0.453π)───FSim(0.25π, 0)───Rz(0.538π)──── │(0, 6): ───Rz(0.507π)────FSim(0.25π, 0)───Rz(-0.484π)───
复制代码
(四)总结
目前量子技术处于高速发展的时代,Cirq 框架能够很好的适应过渡期,为 NISQ 新时代带来许多有参考意义的发展方向,以及对量子生态建设做出贡献。凭借行业内各公司的不断努力,相信在不久的将来,也会有越来越多成熟的产品出现在量子领域,让我们拭目以待吧。
评论