动态量子电路¶
PennyLane 允许在量子电路中间使用测量。这种测量称为中间电路测量,可以用来动态地塑造电路的结构,并在电路执行过程中获取有关量子状态的信息。
可用功能¶
中途电路测量¶
在PennyLane中执行中路测量的函数是
measure(),可以如下使用:
dev = qml.device("default.qubit")
@qml.qnode(dev)
def my_qnode(x, y):
qml.RY(x, wires=0)
qml.CNOT(wires=[0, 1])
m_0 = qml.measure(1, reset=False, postselect=None)
qml.cond(m_0, qml.RY)(y, wires=0)
return qml.probs(wires=[0]), qml.expval(m_0)
有关measure()、cond()和中间电路测量统计的详细信息,请参阅以下部分,以及有关模拟策略及其配置的信息进一步见下文。更多信息可以在各个方法的文档中找到。也请考虑我们的中间电路测量简介中间电路测量统计收集的操作指南,以及使用中间电路测量创建动态电路的操作指南。
重置量子比特¶
在进行中间电路测量后,导线可以被重用。此外,可以通过在 measure() 中设置 reset=True 将测量过的导线重置为 \(|0 \rangle\) 状态:
dev = qml.device("default.qubit", wires=3)
@qml.qnode(dev)
def func():
qml.PauliX(1)
m_0 = qml.measure(1, reset=True)
qml.PauliX(1)
return qml.probs(wires=[1])
执行这个 QNode:
>>> func()
tensor([0., 1.], requires_grad=True)
中途选择测量¶
PennyLane 还支持通过指定 postselect 关键字参数来对中间电路测量结果进行后选择,使用 measure()。默认情况下,后选择会丢弃与 postselect 参数不匹配的结果。例如,指定 postselect=1 相当于将状态矢量投影到 \(|1\rangle\) 状态,即不考虑测量结果为 \(|0\rangle\) 的所有结果:
dev = qml.device("default.qubit")
@qml.qnode(dev)
def func(x):
qml.RX(x, wires=0)
m_0 = qml.measure(0, postselect=1)
return qml.sample(wires=0)
通过对 1 进行后选择,我们只考虑测量结果为 1 的结果。
执行这个QNode 10次得到的结果是
>>> func(np.pi / 2, shots=10)
array([1, 1, 1, 1, 1, 1, 1])
请注意,仅返回7个样本。这是因为不符合后选择标准的样本被丢弃。此行为可以自定义,见 “配置中途测量”部分。
条件运算符¶
用户可以创建通过中间电路测量控制的条件算子,使用cond()。条件算子的条件可以简单地是由measure()调用返回的测量值,或者我们可以基于这些值构建布尔条件并将其传递给cond():
@qml.qnode(dev)
def qnode_conditional_op_on_zero(x, y):
qml.RY(x, wires=0)
qml.CNOT(wires=[0, 1])
m_0 = qml.measure(1)
qml.cond(m_0 == 0, qml.RY)(y, wires=0)
return qml.probs(wires=[0])
pars = np.array([0.643, 0.246], requires_grad=True)
>>> qnode_conditional_op_on_zero(*pars)
tensor([0.88660045, 0.11339955], requires_grad=True)
有关更多示例,请参考 cond() 文档和 关于使用中间电路测量创建动态电路的操作指南。
中间电路测量统计¶
中途测量的统计数据可以与终端测量统计数据一起收集。 目前,counts()、expval()、probs()、sample() 和 var() 被支持。
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def func(x, y):
qml.RX(x, wires=0)
m_0 = qml.measure(0)
qml.cond(m_0, qml.RY)(y, wires=1)
return qml.probs(wires=1), qml.probs(op=m_0)
执行这个 QNode:
>>> func(np.pi / 2, np.pi / 4)
(tensor([0.9267767, 0.0732233], requires_grad=True),
tensor([0.5, 0.5], requires_grad=True))
用户还可以收集通过算术/布尔运算符操控的中间测量的统计数据。这适用于一元运算符和二元运算符。要查看支持的运算符的完整列表,请参考measure()文档。以下是收集此类统计数据的示例:
import pennylane as qml
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit(phi, theta):
qml.RX(phi, wires=0)
m_0 = qml.measure(wires=0)
qml.RY(theta, wires=1)
m_1 = qml.measure(wires=1)
return qml.sample(~m_0 - 2 * m_1)
正在执行这个 QNode:
>>> circuit(1.23, 4.56, shots=5)
array([-1, -2, 1, -1, 1])
支持收集使用算术/布尔运算符操作的中途测量的统计数据,使用 counts()、 expval()、 sample() 和 var()。
此外,可以通过将多个中间电路测量值的列表传递给测量过程来收集多个中间电路测量的统计数据:
import pennylane as qml
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit(phi, theta):
qml.RX(phi, wires=0)
m_0 = qml.measure(wires=0)
qml.RY(theta, wires=1)
m_1 = qml.measure(wires=1)
return qml.sample([m_0, m_1])
执行这个 QNode:
>>> circuit(1.23, 4.56, shots=5)
array([[0, 1],
[1, 1],
[0, 1],
[0, 0],
[1, 1]])
中途测量序列的统计收集通过counts()、probs()和sample()支持。
警告
在收集一系列中间电路测量的统计数据时,该序列不得包含算术表达式。
仿真技术¶
PennyLane 目前提供三种方法在经典计算机上模拟中途测量:延迟测量原理、动态一次性采样和树遍历方法。这些方法在内存需求、计算成本以及与其他功能(如 shots 和微分方法)的兼容性上有所不同。虽然这些要求取决于模拟的细节,但关于中途测量(和 shots)数量的预期比例是
模拟技术 |
内存 |
时间 |
微分 |
shots |
解析 |
|---|---|---|---|---|---|
延迟测量 |
\(\mathcal{O}(2^{n_{MCM}})\) |
\(\mathcal{O}(2^{n_{MCM}})\) |
是 \({}^1\) |
是 |
是 |
动态一次性 |
\(\mathcal{O}(1)\) |
\(\mathcal{O}(n_{shots})\) |
有限差分\({}^2\) |
是 |
否 |
树遍历 |
\(\mathcal{O}(n_{MCM}+1)\) |
\(\mathcal{O}(min(n_{shots}, 2^{n_{MCM}}))\) |
有限差分\({}^2\) |
是 |
否 |
\({}^1\) 反向传播和有限差分完全支持。如果不使用后选择方法,则支持伴随方法和参数偏移规则。
\({}^2\) 在原则上,只要不使用后选择,就支持参数偏移微分。条件应用操作中的参数将回退到有限差分,因此应提供适当的 h 值(见 finite_diff())。
仿真技术的优缺点差异很大,最佳技术将取决于仿真工作流程的细节。一般来说:
动态一次性采样在多次测量-少量样本的情况下表现出色,
树遍历技术可以处理具有许多射击和测量的大规模模拟,
延迟测量是一种通用解决方案,能够在(几乎)所有情况下支持中途测量,但代价是巨大的内存开销。它是唯一支持解析模拟的方法。
默认情况下,QNode在没有执行和有执行次数的情况下分别使用延迟测量和动态单次采样(如果支持)。该方法可以通过关键字参数 mcm_method 在 QNode 创建时进行配置(见 “配置中途电路测量”)。
延迟测量¶
具有中间电路测量的量子函数可以通过延迟测量原理来执行。在PennyLane中,此技术可通过mcm_method="deferred"或作为变换defer_measurements()来使用。
延迟测量原理提供了一种强大的方法来模拟中间电路测量、条件操作和测量统计,以可微分和设备无关的方式。它为电路中的每个中间测量添加一个辅助量子比特,这导致内存和模拟时间的开销随着测量次数的增加而呈指数级增长。
>>> deferred_qnode = qml.defer_measurements(my_qnode)
>>> pars = np.array([0.643, 0.246])
>>> deferred_qnode(*pars)
(tensor([0.90165331, 0.09834669], requires_grad=True),
tensor(0.09984972, requires_grad=True))
延迟测量的效果变得清晰,如果我们在应用变换之前和之后画出QNode:
>>> print(qml.draw(my_qnode)(*pars))
0: ──RY(0.64)─╭●───────RY(0.25)─┤ Probs
1: ───────────╰X──┤↗├──║────────┤
╚═══╩════════╡ <MCM>
>>> print(qml.draw(deferred_qnode)(*pars))
0: ──RY(0.64)─╭●────╭RY(0.25)─┤ Probs
1: ───────────╰X─╭●─│─────────┤
2: ──────────────╰X─╰●────────┤ <None>
中间电路测量被推迟到电路的末尾,条件应用操作变为(量子)控制操作。
动态一次性采样¶
原生支持中途测量的设备可以通过一次性执行动态电路来评估动态电路,为每次执行采样一个动态执行路径。
在PennyLane中,这种技术可以通过QNode参数 mcm_method="one-shot" 或作为变换 dynamic_one_shot() 实现。顾名思义,这个变换仅适用于执行有限拍的 QNode,并且要求设备本身支持中间电路测量。
与defer_measurements()变换相比,dynamic_one_shot()变换通常在许多中间电路测量和少量尝试的限制下更具优势。这是因为,与延迟测量原理不同,该方法不需要为电路中的每个中间电路测量增加额外的连接线。
警告
使用射击执行的动态电路应该通过有限差分法进行区分。
树遍历算法¶
动态电路执行类似于遍历二叉树,其中每个中间电路测量对应于一个节点,而它们之间的门对应于边。树遍历算法深度优先地探索这棵树。它改进了上述动态单次方法,该方法在每次实验中从头到尾模拟一个随机选择的分支,通过一次性收集节点或叶子上的所有样本。
在PennyLane中,该技术可通过QNode参数 mcm_method="tree-traversal" 获得;它不是一个变换。
树遍历算法结合了一次性方法在内存上的指数节省与延迟测量的取样效率。
忽略开销,模拟所有分支所需的计算量与 defer_measurements() 相同,但没有 \(O(2^{n_{MCM}})\) 的内存成本。为了节省时间,每次中途电路测量时都会复制状态向量,这需要 \(n_{MCM}+1\) 个状态向量,相较于 defer_measurements() 是指数级的改进。
由于许多节点的计数在基于射击的模拟中为零,通常可以忽略整个子树,从而减少计算成本。
警告
树遍历算法仅支持
DefaultQubit 设备,目前不支持即时 (JIT) 编译。
配置中间电路测量¶
如上所述,对于在PennyLane中具有中间电路测量的电路,有多种模拟技术。它们在初始化QNode时可以通过以下关键字进行配置:
mcm_method: 设置用于应用中间电路测量的方法。选项包括"deferred"、"one-shot"和"tree-traversal",对应上面描述的三种技术。 默认情况下,当执行时带有shots时,mcm_method="one-shot",否则为"deferred"。 使用qjit()时,还有一个额外的(默认)选项mcm_method="single-branch-statistics",它随机探索执行 树的单一分支。警告
如果提供了
mcm_method参数,则不得手动将延迟测量或动态单次抽样的转换应用于QNode。postselect_mode:配置在使用有限次测量电路进行中途选择测量时如何处理无效的测量结果。使用"hw-like"来丢弃无效样本。在这种情况下,处理结果时使用的样本数可能少于总的测量次数。使用"fill-shots"来无条件取样后选择的值,仅创建有效样本。这相当于重复取样,直到有效样本的数量与总测量次数相匹配。默认值是"hw-like"。dev = qml.device("default.qubit", wires=3, shots=10) def circ(): qml.Hadamard(0) m_0 = qml.measure(0, postselect=1) return qml.sample(qml.PauliZ(0)) fill_shots = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="fill-shots") hw_like = qml.QNode(circ, dev, mcm_method="one-shot", postselect_mode="hw-like")
>>> fill_shots() array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]) >>> hw_like() array([-1., -1., -1., -1., -1., -1., -1.])
注意
当使用
jax接口时,后选择模式"hw-like"将改变模拟技术的行为。对于动态一次性,无效的射击不会被丢弃,而是会被替换为
np.iinfo(np.int32).min。它们将不会用于处理最终结果(例如期望值),但它们会出现在直接返回样本的QNode的输出中。使用
jax.jit时,组合"deferred"和"hw-like"是不支持的, 由于defer_measurements()变换的限制。这个行为 在未来会改变。