qml.compiler

该模块提供混合量子-经典编译的支持。通过使用qjit()装饰器,整个工作流可以进行即时(JIT)编译——包括量子和经典处理——在首次函数执行时编译为机器二进制文件。对编译后的函数的后续调用将执行之前编译的二进制文件,从而显著提高性能。

目前,PennyLane 支持 Catalyst 混合编译器 和 CUDA 量子编译器 以及 qjit() 装饰器。Catalyst 的一个显著优点是能够在编译过程中保留与量子 操作相关的复杂控制流——例如 if 语句和 for 循环,以及包含测量 反馈——同时继续支持端到端的自动微分。

注意

Catalyst 目前仅支持 PennyLane 的 JAX 接口。

概述

PennyLane中混合编译的主要入口点是通过qjit()装饰器,该装饰器可以与其他编译器特定的装饰器和函数混合使用:

qjit([fn, compiler])

PennyLane中用于即时编译混合量子程序的装饰器。

for_loop([开始, ]结束[, 步长])

A qjit() 适用于 PennyLane 程序的兼容 for 循环。

while_loop(cond_fn)

A qjit() 适用于 PennyLane 程序的兼容 for 循环。

jvp(f, params, tangents[, method, h, argnum])

PennyLane程序的qjit()兼容的雅可比-向量乘积。

vjp(f, params, cotangents[, method, h, argnum])

PennyLane程序的qjit()兼容的向量-雅可比积。

此外,还有几个开发者函数可用于探测可用的混合编译器。

available_compilers()

加载并返回已安装且与qjit()装饰器兼容的可用编译器列表。

available([编译器])

检查给定编译器包的可用性。

active_compiler()

检查在qjit()评估上下文中激活了哪个编译器。

active()

检查调用者是否在 qjit() 评估上下文中。

下面呈现的是与qjit()兼容的PennyLane原语的列表。

adjoint(fn[, lazy])

创建一个算子的伴随或一个应用所提供的函数伴随的函数。

cond(条件[, 真值函数, 假值函数, elif条])

量子兼容的if-else条件 --- 基于中途电路量子比特测量结果等参数对量子操作进行条件处理。

ctrl(op, control[, control_values, work_wires])

创建一个方法,该方法应用所提供操作的受控版本。

grad(func[, argnum, method, h])

返回作为混合量子-经典函数的可调用函数的梯度。

jacobian(函数[, 参数编号, 方法, 步长])

返回雅可比矩阵,作为向量值(QNodes的函数)的可调用函数。

编译器

编译器模块提供了将外部混合量子-经典编译器与PennyLane集成的基础设施,但不提供内置编译器。

目前,仅支持与PennyLane一起使用的Catalyst混合编译器和CUDA Quantum编译器工具链,然而计划在不久的将来引入其他编译器。

注意

要安装 Catalyst,只需运行以下 pip 命令:

pip install pennylane-catalyst

有关更多信息和支持的平台,请参阅安装指南。

基本用法

注意

Catalyst 支持的后端设备包括 lightning.qubit, lightning.kokkos, lightning.gpu, 和 braket.aws.qubit, 但 不支持 default.qubit

有关支持的设备的完整列表,请参见 支持的设备

使用即时编译(JIT)时,编译在量子函数第一次执行时的调用位置触发。例如,circuit 在第一次调用时被编译。

dev = qml.device("lightning.qubit", wires=2)

@qml.qjit
@qml.qnode(dev)
def circuit(theta):
    qml.Hadamard(wires=0)
    qml.RX(theta, wires=1)
    qml.CNOT(wires=[0,1])
    return qml.expval(qml.Z(1))
>>> circuit(0.5)  # the first call, compilation occurs here
array(0.)
>>> circuit(0.5)  # the precompiled quantum function is called
array(0.)

或者,如果提供了参数类型提示,编译可以在函数被修饰时“提前”发生。

from jax.core import ShapedArray

@qml.qjit  # compilation happens at definition
@qml.qnode(dev)
def circuit(x: complex, z: ShapedArray(shape=(3,), dtype=jnp.float64)):
    theta = jnp.abs(x)
    qml.RY(theta, wires=0)
    qml.Rot(z[0], z[1], z[2], wires=0)
    return qml.state()
>>> circuit(0.2j, jnp.array([0.3, 0.6, 0.9]))  # calls precompiled function
array([0.75634905-0.52801002j, 0. +0.j,
    0.35962678+0.14074839j, 0. +0.j])

Catalyst编译器还支持在编译程序中捕获命令式Python控制流,这使得控制流在运行时而不是在编译时在Python中被解释。您可以通过autograph=True关键字参数启用此功能。

@qml.qjit(autograph=True)
@qml.qnode(dev)
def circuit(x: int):

    if x < 5:
        qml.Hadamard(wires=0)
    else:
        qml.T(wires=0)

    return qml.expval(qml.Z(0))
>>> circuit(3)
array(0.)
>>> circuit(5)
array(1.)

请注意,AutoGraph 会导致额外的限制,特别是在涉及全局状态时。有关支持和不支持的使用案例的完整讨论,请参阅AutoGraph 指南

有关使用 qjit() 装饰器和 Catalyst 与 PennyLane 的更多详细信息,请参阅 Catalyst 快速入门指南, 以及 调试技巧和要点 页面,以了解 Catalyst 和 PennyLane 之间的差异,以及 如何最佳地构建工作流以提高使用 Catalyst 时的性能。

添加编译器

警告

PennyLane 编译器 API 是实验性的,可能会有所变化。

要注册任何编译器软件包,可以使用一个实验性的接口。该接口在指定的组名 pennylane.compilers 下公开 entry_points 元数据,包括以下入口点:

  • compiler_name.context": 编译评估上下文管理器的路径。 这个上下文管理器应该有一个方法 context.is_tracing(), 如果在被跟踪或捕获的程序中调用,返回 True

  • compiler_name.ops: 编译器操作模块的路径。这个操作模块可能包含特定于编译器的PennyLane操作版本,例如 cond()measure()adjoint()。在JIT上下文中,PennyLane操作可能会调度到这些函数。

  • compiler_name.qjit:编译器提供的JIT装饰器的路径。 该装饰器应具有以下签名qjit(fn, *args, **kwargs), 其中fn是要被编译的函数。

其中 compiler_name 应替换为编译器的名称。 例如,对于 Catalyst,我们定义入口点 catalyst.contextcatalyst.opscatalyst.qjit。这允许 catalyst 包定义多个编译器。

编译器的名称可以被用户用来指示应使用哪个编译器。例如:

@qml.qjit(compiler="catalyst")
def function(x, y):
    ...

@qml.qjit(compiler="compiler_name")
def function(x, y):
    ...

为了支持在有和没有参数的情况下应用 qjit 装饰器,

@qml.qjit
def function(x, y):
    ...

@qml.qjit(verbose=True, additional_args, ...)
def function(x, y):
    ...

您应该确保 qjit 装饰器本身返回一个装饰器,如果没有提供函数:

def qjit(fn=None, **kwargs):
    if fn is not None:
        return compile_fn(fn, **kwargs)

    def wrapper_fn(fn):
        return compile_fn(fn, **kwargs)

    return wrapper_fn