测量

PennyLane 可以从量子设备中提取不同类型的测量结果:可观测量的期望值、它的方差、单次测量的样本或计算基态的概率。

例如,以下电路返回在导线 1 上的 PauliZ 可观察量的期望值:

def my_quantum_function(x, y):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=1)
    return qml.expval(qml.PauliZ(1))

可用的测量函数有

expval(op)

提供的可观察量的期望值。

sample([操作, 导线])

来自提供的可观察对象的样本,样本数量由相应设备的 dev.shots 属性确定,返回原始样本。

counts([op, wires, all_outcomes])

从提供的观测值中获取样本,样本数量由对应设备的 dev.shots 属性决定,返回每个样本的计数数量。

var(op)

提供的可观察量的方差。

probs([wires, op])

每个计算基态的概率。

state()

计算基中的量子态。

density_matrix(电线)

计算基中的量子密度矩阵。

vn_entropy(wires[, log_base])

测量前系统的冯·诺依曼熵。

mutual_info(wires0, wires1[, log_base])

测量之前子系统之间的互信息:

purity(导线)

测量前系统的纯度。

classical_shadow(导线[, 种子])

经典阴影测量协议。

shadow_expval(H[, k, seed])

以可微分的方式使用经典阴影计算期望值。

注意

所有测量函数都支持解析微分,除了 sample(), counts()classical_shadow(),因为它们返回 随机 结果。

组合测量

量子函数也可以返回多个可观测量的组合测量。如果可观测量不是量子比特逐个可交换的,那么可能会在后台进行多个设备执行。不可交换的可观测量不能与非可观测类型的测量同时进行,例如 sample(), counts(), probs(), state(), 和 density_matrix()

def my_quantum_function(x, y):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=1)
    return qml.expval(qml.PauliZ(0)), qml.var(qml.PauliX(0))

您还可以使用列表推导式和其他常见的Python模式:

def my_quantum_function(x, y):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=1)
    return [qml.expval(qml.PauliZ(i)) for i in range(2)]

作为联合测量的完整示例,让我们看一下一个贝尔态 \((|00\rangle + |11\rangle)/\sqrt{2}\),由一个 Hadamard 门和一个 CNOT 门制备。

import pennylane as qml
from pennylane import numpy as np

dev = qml.device("default.qubit", wires=2, shots=1000)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1))

第一个和第二个量子比特的联合PauliZ测量返回一个包含两个数组的元组,每个数组包含相应量子比特的测量结果。 sample() 返回每个可观测量的1000个样本,如设备上定义的。

>>> results = circuit()
>>> results[0].shape
(1000,)
>>> results[1].shape
(1000,)

由于这两个量子位是完全纠缠的,测量结果总是重合的,因此列表是相等的:

>>> np.all(result[0] == result[1])
True

张量可观测量

PennyLane 支持通过使用 @ 符号测量算子的张量积。例如,要测量\(Z\otimes I \otimes X\) 的期望值:

def my_quantum_function(x, y):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=1)
    qml.CNOT(wires=[0, 2])
    return qml.expval(qml.PauliZ(0) @ qml.PauliX(2))

请注意,我们不需要在电线1上声明身份可观测量;这是隐含假设的。

张量可观察量表示法可以在所有接受可观察量作为参数的测量函数中使用, 包括 expval()var()sample()

计数

为了避免处理长数组以应对大量的拍摄次数,可以使用 counts() 而不是 sample()。这执行与抽样相同的测量,但返回一个包含可能测量结果及其每个结果出现次数的字典,而不是所有结果的列表。

前面的示例将修改如下:

dev = qml.device("default.qubit", wires=2, shots=1000)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.counts(qml.PauliZ(0)), qml.counts(qml.PauliZ(1))

执行电路后,我们可以直接看到每个测量结果发生的次数:

>>> circuit()
({-1: 496, 1: 504}, {-1: 496, 1: 504})

同样,如果未提供观测量,则返回观测到的计算基态的计数。

dev = qml.device("default.qubit", wires=2, shots=1000)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.counts()

结果是:

>>> circuit()
{'00': 495, '11': 505}

默认情况下,仅观察到的结果会包含在字典中。kwarg all_outcomes=True 可以用于显示所有可能的结果,包括在采样中观察到 0 次的结果。

例如,我们可以使用 all_outcomes=True 运行之前的电路:

dev = qml.device("default.qubit", wires=2, shots=1000)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.counts(all_outcomes=True)
>>> result = circuit()
>>> print(result)
{'00': 518, '01': 0, '10': 0, '11': 482}

注意:对于复杂的哈密顿量,这可能会增加相当大的开销时间(因为计算特征值以确定可能的结果的成本),并且随着量子位数量的增加,显示可能的计算基态的输出字典的长度会迅速增长。

如果获得了与其他测量函数一起的计数,而不是 sample(),则返回一个元组以提供 QNodes 输出的可微性。

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0,1])
    qml.X(1)
    return qml.expval(qml.PauliZ(0)),qml.expval(qml.PauliZ(1)), qml.counts()
>>> circuit()
(-0.036, 0.036, {'01': 482, '10': 518})

概率

您也可以通过使用probs()测量函数在计算基概率上训练QNode。该函数可以接受指定的wires或一个旋转计算基的可测量量。

def my_quantum_function(x, y):
    qml.RZ(x, wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=1)
    qml.CNOT(wires=[0, 2])
    return qml.probs(wires=[0, 1])

例如:

>>> dev = qml.device("default.qubit", wires=3)
>>> qnode = qml.QNode(my_quantum_function, dev)
>>> qnode(0.56, 0.1)
array([0.99750208, 0.00249792, 0.        , 0.        ])

返回的概率数组使用字典顺序,因此对应于测量状态\(|00\rangle\)\(99.75\%\)概率,以及测量状态\(|01\rangle\)\(0.25\%\)概率。

改变拍摄次数

对于硬件设备而言,拍摄次数决定了期望值和方差的准确性,以及返回的样本数量,有时执行相同的 QNode 并使用不同的拍摄次数是方便的。

对于像 default.qubit 这样的模拟器,如果我们将 shots 设置为正整数,则将模拟有限次实验。

射击编号可以在设备上更改,或者在执行QNode时通过shots关键字参数暂时更改:

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

@qml.qnode(dev)
def circuit(x, y):
    qml.RX(x, wires=0)
    qml.RY(y, wires=0)
    return qml.expval(qml.PauliZ(0))

# execute the QNode using 10 shots
result = circuit(0.54, 0.1)

# execute the QNode again, now using 1 shot
result = circuit(0.54, 0.1, shots=1)

随着拍摄次数的增加,测量样本的平均值收敛于可观察量的精确期望值。考虑以下电路:

# fix seed to make results reproducible
np.random.seed(1)

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

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    return qml.expval(qml.PauliZ(0))

使用 shots=None 运行模拟器会返回精确的期望值。

>>> circuit(shots=None)
0.0

现在我们设置设备返回随机结果,并将拍摄次数从 10 开始增加。

>>> circuit(shots=10)
0.2
>>> circuit(shots=1000)
-0.062
>>> circuit(shots=100000)
0.00056

结果收敛于精确期望。