qml.pulse

脉冲编程在各种量子系统中用于对量子操作进行低级控制。应用调谐到特征能量的时变电磁场,导致时变的哈密顿相互作用 \(H(t)\)。在固定时间窗口内使用这样的电磁场驱动系统是一种 脉冲程序。该脉冲程序可以调节以实现用于量子计算的高级门。

pulse 模块提供用于模拟量子系统脉冲级控制的函数和类。

它包含一个 ParametrizedHamiltonianParametrizedEvolution 类,用于描述时间依赖的哈密顿相互作用。 pulse 模块还包括多个方便的函数用于定义脉冲。

pulse 模块是为 jax 编写的,无法与PennyLane中通常遇到的其他机器学习框架一起使用。它需要单独安装,详见 jax.readthedocs.io

有关PennyLane中基本脉冲功能和运行ctrl-VQE示例的演示,请参阅我们的differentiable pulse programming演示。

概述

时间演化类

ParametrizedHamiltonian(coeffs, observables)

可调用对象,包含表示参数化哈密顿量的信息。

ParametrizedEvolution(H[, params, t, ...])

参数化演化门,通过将一个 ParametrizedHamiltonian 传递给 evolve() 函数创建

便利函数

constant(标量, 时间)

返回给定的 标量,用于定义具有可训练系数的 ParametrizedHamiltonian

pwc(时间跨度)

接受一个时间跨度并返回一个可调用对象,用于创建一个在时间上分段常量的函数。

pwc_from_function(时间跨度, 箱数)

装饰一个光滑的函数,创建一个分段常数函数来近似它。

rect(x[, windows])

接受一个标量或标量值函数x,并对其应用一个矩形窗口,使得返回的函数在窗口内为x,窗口外为0。

硬件兼容的哈密顿量

rydberg_interaction(register[, wires, ...])

返回一个 ParametrizedHamiltonian,表示由于Rydberg阻塞而导致的Rydberg原子集群的相互作用

rydberg_drive(amplitude, phase, detuning, wires)

返回一个 ParametrizedHamiltonian,表示驱动激光场的作用

transmon_interaction(qubit_freq, ...[, ...])

返回一个 ParametrizedHamiltonian,表示超导跨导系统的电路量子电动力学哈密顿量。

transmon_drive(amplitude, phase, freq, wires)

返回一个ParametrizedHamiltonian,表示一个跨越量子比特的驱动项。

创建参数化的哈密顿量

pulse 模块提供了一个框架,用于创建如下形式的时间依赖哈密顿量

\[H(\{v_j\}, t) = H_\text{drift} + \sum_j f_j(v_j, t) H_j\]

使用常量运算符 \(H_j\) 和可能依赖于参数 \(p\) 和时间 \(t\) 的标量函数 \(f_j(v_j, t)\)

定义一个 ParametrizedHamiltonian 需要系数和算符,其中一些系数是可调用的。定义参数化系数的可调用函数必须具有调用签名 (p, t),其中 p 可以是 floatlistjnp.array。这些函数应在相关情况下使用 jax.numpy 而不是 numpy 定义。

import pennylane as qml
from jax import numpy as jnp

# defining the coefficients fj(p, t) for the two parametrized terms
f1 = lambda p, t: p * jnp.sin(t) * (t - 1)
f2 = lambda p, t: p[0] * jnp.cos(p[1]* t ** 2)

# defining the operations for the three terms in the Hamiltonian
XX = qml.X(0) @ qml.X(1)
YY = qml.Y(0) @ qml.Y(1)
ZZ = qml.Z(0) @ qml.Z(1)

有两种方法可以从系数和算子构造一个 ParametrizedHamiltonian

# Option 1
H1 =  2 * XX + f1 * YY + f2 * ZZ

# Option 2
coeffs = [2, f1, f2]
ops = [XX, YY, ZZ]
H2 =  qml.dot(coeffs, ops)

警告

通过参数化系数的列表初始化一个 ParametrizedHamiltonian 时,可以使用 lambda 函数迭代地创建多个相同形式的系数列表,即:

coeffs = [lambda p, t: p * t for _ in range(3)]

在使用列表推导式定义系数时要小心。避免这样做 coeffs = [lambda p, t: p * t**i for i in range(3)],这将只使用最终的索引 i=2lambda 中,因此表现为 coeffs = [(lambda p, t: p * t**2)] * 3。相反,使用 coeffs = [lambda p, t, power=i: p * t**power for i in range(3)]

ParametrizedHamiltonian是一个可调用的,可以返回一个Operator,如果传递一组参数和一个评估系数的时间\(f_j\)

>>> H1
(
    2 * X(0) @ X(1)
  + <lambda>(params_0, t) * Y(0) @ Y(1)
  + <lambda>(params_1, t) * Z(0) @ Z(1)
)
>>> params = [1.2, [2.3, 3.4]]  # f1 takes a single parameter, f2 takes 2
>>> H1(params, t=0.5)
(
    2 * (X(0) @ X(1))
  + -0.2876553231625218 * (Y(0) @ Y(1))
  + 1.517961235535459 * (Z(0) @ Z(1))
)

在传递参数时,确保系数函数的顺序和参数的顺序匹配。

当初始化一个 ParametrizedHamiltonian 时,定义有固定系数的项必须在参数化项之前,以防止连线顺序的不一致。

注意

ParametrizedHamiltonian 必须在所有时刻保持厄米特性。这一点不会被显式检查;确保哈密顿量定义正确是用户的责任。

参数化演化

在一个跨越时间 \((t_0, t_1)\) 的脉冲程序中,状态根据时间依赖的薛定谔方程演变

\[\frac{\partial}{\partial t} |\psi\rangle = -i H(t) |\psi\rangle\]

实现输入态的单位演化 \(U(t_0, t_1)\),即。

\[|\psi(t_1)\rangle = U(t_0, t_1) |\psi(t_0)\rangle\]

一个 ParametrizedEvolution 是这个解决方案 \(U(t_0, t_1)\) 对于时间依赖的 薛定谔方程的 ParametrizedHamiltonian

ParametrizedEvolution类使用数值常微分方程求解器(见jax.experimental.ode)。它可以通过evolve()函数创建:

from jax import numpy as jnp

f1 = lambda p, t: p * jnp.sin(t) * (t - 1)
H = 2 * qml.X(0) + f1 * qml.Y(1)
ev = qml.evolve(H)
>>> ev
ParametrizedEvolution(wires=[0, 1])

初始 ParametrizedEvolution 没有定义参数,因此不会有定义的矩阵。为了获得一个带有矩阵的 Operator,我们必须传递参数和时间间隔:

>>> ev([1.2], t=[0, 4]).matrix()
Array([[-0.14115842+0.j        ,  0.03528605+0.j        ,
         0.        -0.95982337j,  0.        +0.23993255j],
       [-0.03528605+0.j        , -0.14115842+0.j        ,
         0.        -0.23993255j,  0.        -0.95982337j],
       [ 0.        -0.95982337j,  0.        +0.23993255j,
        -0.14115842+0.j        ,  0.03528605+0.j        ],
       [ 0.        -0.23993255j,  0.        -0.95982337j,
        -0.03528605+0.j        , -0.14115842+0.j        ]],      dtype=complex64)

通过使用不同的输入再次调用ParametrizedEvolution,可以更新参数。

可以通过关键字参数将有关矩阵计算的附加选项传递给ParametrizedEvolution以及参数:

>>> qml.evolve(H)(params=[1.2], t=[0, 4], atol=1e-6, mxstep=1)
ParametrizedEvolution(Array(1.2, dtype=float32, weak_type=True), wires=[0, 1])

可用的关键字参数可以在 ParametrizedEvolution 中找到。如果未指定,它们将默认为预定值。

在 QNode 中使用 qml.evolve

可以在QNode中实现ParametrizedEvolution。我们将演化以下ParametrizedHamiltonian

from jax import numpy as jnp

f1 = lambda p, t: jnp.sin(p * t)
H = f1 * qml.Y(0)

现在我们可以在QNode中执行这个哈密顿哈密顿演变并计算它的梯度:

import jax

jax.config.update("jax_enable_x64", True)

dev = qml.device("default.qubit", wires=1)

@jax.jit
@qml.qnode(dev, interface="jax")
def circuit(params):
    qml.evolve(H)(params, t=[0, 10])
    return qml.expval(qml.Z(0))
>>> params = [1.2]
>>> circuit(params)
Array(0.96632722, dtype=float64)
>>> jax.grad(circuit)(params)
[Array(2.35694829, dtype=float64)]

我们可以使用装饰器 jax.jit 来即时编译这个执行。这意味着第一次执行通常会花费更多时间,而所有后续的执行将显著更快。 JIT 编译是可选的,当只有单次执行时可以去掉该装饰器。有关即时编译的更多信息,请参见 jax 文档。

警告

要找到两个算子的同时演化,重要的是它们必须包含在同一个 evolve() 中。对于两个不对易的 ParametrizedHamiltonian,应用 qml.evolve(H1)(params, t=[0, 10]) 然后跟随 qml.evolve(H2)(params, t=[0, 10]) 使这两个脉冲同时作用,尽管时间窗口重叠。相反,它们将在同一时间段内演化,但不考虑 H1 的演化如何影响 H2

请查看ParametrizedEvolution的使用详情以获取详细示例。