更新的操作符

在PennyLane的版本0.36中,我们更改了一些操作符及其之间的算术运算的后台处理方式。这实现了几个目标:

  1. 让使用PennyLane运算符和使用笔和纸一样简单。

  2. 提高运算符算术的效率。

在许多情况下,这些变化不应导致代码出现问题,且与以前版本的差异可能不明显。

此页面提供有关运算符算术更新的附加详细信息,并可用于排查受影响用户的问题。

注意

如果你在寻找快速解决方案,请跳转到故障排除部分!

访问故障排除部分后,如果您仍然卡住,则可以:

  • 在PennyLane 讨论论坛上发布。

  • 如果您怀疑您的问题是由于PennyLane本身的漏洞,请在PennyLane GitHub页面上打开一个 错误报告

更新摘要

经过一段时间的弃用,运算符的遗留行为在PennyLane版本0.40中被移除。 使用最新PennyLane的任何人将自动使用更新的运算符算术。 理想情况下,您的代码在进行此更新时不应出现崩溃。 如果仍然出现,可能只需要一些小的更改。 为此,请参阅故障排除部分。 如果您使用了任何明确提供的函数以继续使用被弃用的行为,如qml.operation.disable_new_opmath(),该代码将需要被移除。

  • 执行算术运算的底层系统已被更改。可以使用标准的Python操作符进行算术运算,如 +, *@,或者通过位于 op_math 的算术函数。

  • 您现在可以通过 I, X, Y, 和 Z 轻松访问 Pauli 运算符。

    >>> from pennylane import I, X, Y, Z
    >>> X(0)
    X(0)
    

    原始的长名称 Identity, PauliX, PauliY, 和 PauliZ 仍可用,且在功能上等同于 I, X, Y, 和 Z,但是现在推荐使用短名称。

  • PennyLane中的算子可以具有后端的Pauli表示,这可以用来执行更快速的算子运算。现在,当可用时,Pauli表示将自动用于计算。您可以在可用时访问任何算子的pauli_rep属性。

    >>> op = X(0) + Y(0)
    >>> op.pauli_rep
    1.0 * X(0)
    + 1.0 * Y(0)
    >>> type(op.pauli_rep)
    pennylane.pauli.pauli_arithmetic.PauliSentence
    

    您可以通过operation()方法将PauliSentence转换回适当的Operator

    >>> op.pauli_rep.operation()
    X(0) + Y(0)
    
  • 对PennyLane算子的字符串表示进行了大量改进,使其更短,并可以复制粘贴为有效的PennyLane代码。

    >>> 0.5 * X(0)
    0.5 * X(0)
    >>> 0.5 * (X(0) + Y(1))
    0.5 * (X(0) + Y(1))
    

    包含多个项的和被分成多行,但仍然可以复制回来作为有效的代码:

    >>> 0.5 * (X(0) @ X(1)) + 0.7 * (X(1) @ X(2)) + 0.8 * (X(2) @ X(3))
    (
        0.5 * (X(0) @ X(1))
      + 0.7 * (X(1) @ X(2))
      + 0.8 * (X(2) @ X(3))
    )
    

旧系统和新系统之间的变化主要涉及 Python 运算符 + - * / @,现在创建以下 Operator 子类实例。

旧版

更新后的运算符

张量积 X(0) @ X(1)

operation.Tensor

ops.Prod

总和 X(0) + X(1)

ops.Hamiltonian

ops.Sum

标量积 1.5 * X(1)

ops.Hamiltonian

ops.SProd

qml.dot(coeffs,ops)

ops.Sum

ops.Sum

qml.Hamiltonian(coeffs, ops)

ops.Hamiltonian

ops.LinearCombination

qml.ops.LinearCombination(coeffs, ops)

不适用

ops.LinearCombination

三个主要的新 opmath 类 SProdProdSum 已经存在了一段时间。比如,dot() 一直返回一个 Sum 实例。

使用方法

除了python操作符,你还可以使用构造函数 s_prod()prod()sum()。对于复合操作符,我们可以通过 op.operands 属性访问它们的组成部分。

>>> op = qml.sum(X(0), X(1), X(2))
>>> op.operands
(X(0), X(1), X(2))

如果所有术语都是由具有有效 pauli_rep 的算子组成,那么复合算子在 PauliSentence 实例的意义上也具有有效的 pauli_rep。这通常对于快速算术处理很有用。

>>> op.pauli_rep
1.0 * X(0)
+ 1.0 * X(1)
+ 1.0 * X(2)

此外,复合运算符可以使用 simplify()op.simplify() 方法简化。

>>> op = 0.5 * X(0) + 0.5 * Y(0) - 1.5 * X(0) - 0.5 * Y(0) # no simplification by default
>>> op.simplify()
-1.0 * X(0)
>>> qml.simplify(op)
-1.0 * X(0)

请注意,简化从不在原地发生,因此原始操作符保持不变。

>>> op
(
    0.5 * X(0)
  + 0.5 * Y(0)
  + -1 * 1.5 * X(0)
  + -1 * 0.5 * Y(0)
)

我们经常对获取系数和 算子列表感兴趣。我们可以通过使用 op.terms() 方法来实现。

>>> op = 0.5 * (X(0) @ X(1) + Y(0) @ Y(1) + 2 * Z(0) @ Z(1)) - 1.5 * I() + 0.5 * I()
>>> op.terms()
([0.5, 0.5, 1.0, -1.0], [X(1) @ X(0), Y(1) @ Y(0), Z(1) @ Z(0), I()])

从这个例子可以看出,这个方法已经处理了算术简化。

qml.哈密顿量

TensorHamiltonian 已被移除。熟悉的 qml.Hamiltonian 仍然可以使用,它会调度到 LinearCombination,并提供相同的用法和功能,但实现细节不同。

>>> import pennylane as qml
>>> from pennylane import X
>>> H = qml.Hamiltonian([0.5, 0.5], [X(0), X(1)])
>>> type(H)
pennylane.ops.op_math.linear_combination.LinearCombination

故障排除

如果您在使用早期版本的PennyLane时存在有效代码,您可能会在版本v0.36及以上中遇到PennyLane更新的算子算术问题。为了帮助识别解决方案,请选择下面描述您情况的选项。

我们建议进行以下检查:

  • 检查是否明确使用了遗留的 Tensor 类。如果在你的脚本中找到它,可以将 Tensor(*terms) 更改为 qml.prod(*terms),调用签名保持不变。

  • 检查显式使用 op.obs 属性的情况,其中 op 是某个操作符。这是访问 Tensor 实例中张量积项的方式。请改用 op.operands

    op = X(0) @ X(1)
    assert op.operands == (X(0), X(1))
    
  • 检查对 qml.ops.Hamiltonian 的显式使用。在这种情况下,只需更改为 qml.Hamiltonian。 这将分派到 LinearCombination 类,它提供相同的 API 和功能 但实现细节不同。

如果由于某些意想不到的原因您的脚本仍然出错,请在PennyLane 讨论论坛上发帖或在PennyLane GitHub页面上提交错误报告

LinearCombination的API与已移除的qml.ops.Hamiltonian的API几乎相同。

一个小的区别是 Hamiltonian.simplify() 不再在原地改变实例。相反,你必须执行

以下:

>>> H1 = qml.Hamiltonian([0.5, 0.5], [X(0) @ X(1), X(0) @ X(1)])
>>> H1 = H1.simplify()

最终操作符的类型由最外层的操作决定。结果对象是一个嵌套结构(和的积/积的和或积的和的积)。

>>> op = 0.5 * (X(0) @ X(1)) + 0.5 * (Y(0) @ Y(1))
>>> type(op)
pennylane.ops.op_math.sum.Sum
>>> op.operands
(0.5 * (X(0) @ X(1)), 0.5 * (Y(0) @ Y(1)))
>>> type(op.operands[0]), type(op.operands[1])
(pennylane.ops.op_math.sprod.SProd, pennylane.ops.op_math.sprod.SProd)
>>> op.operands[0].scalar, op.operands[0].base, type(op.operands[0].base)
(0.5, X(0) @ X(1), pennylane.ops.op_math.prod.Prod)

我们可以构造一个具有不同嵌套结构的等效操作符。

>>> op = (0.5 * X(0)) @ X(1) + (0.5 * Y(0)) @ Y(1)
>>> op.operands
((0.5 * X(0)) @ X(1), (0.5 * Y(0)) @ Y(1))
>>> type(op.operands[0]), type(op.operands[1])
(pennylane.ops.op_math.prod.Prod, pennylane.ops.op_math.prod.Prod)
>>> op.operands[0].operands
(0.5 * X(0), X(1))
>>> type(op.operands[0].operands[0]), type(op.operands[0].operands[1])
(pennylane.ops.op_math.sprod.SProd,
 pennylane.ops.qubit.non_parametric_ops.PauliX)

还有另一种构造相同等效算符的方法。 我们可以通过使用 op.simplify() 将它们都带到同样的格式,从而将算符简化为 \(\sum_i c_i \hat{O}_i\) 的形式,其中 \(c_i\) 是一个标量系数,而 \(\hat{O}_i\) 是一个 纯算符或算符的张量积。

>>> op1 = 0.5 * (X(0) @ X(1)) + 0.5 * (Y(0) @ Y(1))
>>> op2 = (0.5 * X(0)) @ X(1) + (0.5 * Y(0)) @ Y(1)
>>> op3 = 0.5 * (X(0) @ X(1) + Y(0) @ Y(1))
>>> qml.equal(op1, op2), qml.equal(op2, op3), qml.equal(op3, op1)
(True, False, False)
>>> op1 = op1.simplify()
>>> op2 = op2.simplify()
>>> op3 = op3.simplify()
>>> qml.equal(op1, op2), qml.equal(op2, op3), qml.equal(op3, op1)
(True, True, True)
>>> op1, op2, op3
(0.5 * (X(1) @ X(0)) + 0.5 * (Y(1) @ Y(0)),
 0.5 * (X(1) @ X(0)) + 0.5 * (Y(1) @ Y(0)),
 0.5 * (X(1) @ X(0)) + 0.5 * (Y(1) @ Y(0)))

我们也可以通过 op.terms() 方法获得这些标量系数和张量积算子。

>>> coeffs, ops = op1.terms()
>>> coeffs, ops
([0.5, 0.5], [X(1) @ X(0), Y(1) @ Y(0)])

请仔细阅读上述选项。如果您仍然遇到问题,可以:

  • 在 PennyLane 讨论论坛上发布。请包含一整段代码以演示您的问题,以便我们能快速排查。

  • 如果您怀疑您的问题是由于PennyLane本身的错误,请在PennyLane GitHub页面上提交一个 错误报告