qalgora-Q Docs Hub量子文档 ✦ Ask AI✦ 问问文档

Sub-circuit Synthesis — control and adjoint

Two of the most powerful composition tools in qalgora-Q take an entire kernel and apply it as the controlled or inverse version of itself. You write the base operation once; the compiler synthesises the rest.

Controlling a kernel

qalgora.control(kernel, controls, *args) applies kernel only when every control qubit is set. The controls can be a single qubit or a register, and the synthesis strategy is left to the compiler.

import qalgora
from qalgora import h, x

@qalgora.kernel
def x_gate(q: qalgora.qubit):
    x(q)

@qalgora.kernel
def controlled():
    q = qalgora.qvector(3)
    h(q.front(2))            # q.front(2): a qview of the first two qubits
    ctrl_bits = q.front(2)
    # apply x_gate to q[2], controlled on the first two qubits (a Toffoli)
    qalgora.control(x_gate, ctrl_bits, q[2])

q.front(2) returns a qview of the first two qubits; gate operations accept a qview and broadcast element-wise across its qubits, so h(q.front(2)) applies H to both.

Inverting a kernel

qalgora.adjoint(kernel, *args) applies the Hermitian conjugate — every operation reversed and daggered. Essential for uncomputation and for building U† blocks.

from qalgora import rx, h

@qalgora.kernel
def rx_and_h(theta: float, q: qalgora.qubit):
    rx(theta, q)
    h(q)

@qalgora.kernel
def inverted(n: int):
    q = qalgora.qvector(n)
    # apply (rx_and_h)^dagger = h^dagger then rx(-theta)
    qalgora.adjoint(rx_and_h, 3.14159, q[2])

The adjoint reverses the body, so it applies h first and then rx(-theta). Auto-inversion requires the kernel to be reversible — no measurement, reset, or other irreversible operations.

Negative-polarity controls

Prefix a control qubit with ~ to trigger on |0⟩ instead of |1⟩ — handy when a sub-circuit should fire on the absence of an excitation:

# fire `kernel` when qubit0 is |1> AND qubit1 is |0>
qalgora.control(kernel, [qubit0, ~qubit1], kernel_arg)

Here ~qubit1 is a negative-polarity control marker, not Python's bitwise-NOT — it tells the synthesiser to trigger on |0⟩.

Why this matters
Because the compiler owns the synthesis, a controlled kernel is optimised as a whole rather than gate by gate — often producing far fewer two-qubit gates than hand-controlling each operation.

子电路合成 — controladjoint

qalgora-Q 里有两件最趁手的组合工具:它们接收一个完整的内核,把它当作自身的受控版或版来施加。基础操作你只写一遍,剩下的交给编译器去合成。

对内核施加控制

qalgora.control(kernel, controls, *args) 只有在所有控制比特都置位时才施加 kernel。控制端可以是单个量子比特,也可以是一整个寄存器,具体怎么合成由编译器拿主意。

import qalgora
from qalgora import h, x

@qalgora.kernel
def x_gate(q: qalgora.qubit):
    x(q)

@qalgora.kernel
def controlled():
    q = qalgora.qvector(3)
    h(q.front(2))            # q.front(2):返回前两个量子比特的 qview
    ctrl_bits = q.front(2)
    # apply x_gate to q[2], controlled on the first two qubits (a Toffoli)
    qalgora.control(x_gate, ctrl_bits, q[2])

q.front(2) 返回前两个量子比特的 qview;门操作可接受 qview 并自动逐比特广播,因此 h(q.front(2)) 会对这两个比特都施加 H。

对内核求逆

qalgora.adjoint(kernel, *args) 施加内核的厄米共轭——把每个操作倒序排列,再逐个取 dagger 共轭。这对反计算(uncomputation)和搭建 U† 模块都不可或缺。

from qalgora import rx, h

@qalgora.kernel
def rx_and_h(theta: float, q: qalgora.qubit):
    rx(theta, q)
    h(q)

@qalgora.kernel
def inverted(n: int):
    q = qalgora.qvector(n)
    # apply (rx_and_h)^dagger = h^dagger then rx(-theta)
    qalgora.adjoint(rx_and_h, 3.14159, q[2])

adjoint 按相反顺序施加 h 和 rx(-theta)。自动求逆要求内核可逆——不得包含测量、reset 或其它不可逆操作。

负极性控制

在控制比特前加个 ~,它就改成在 |0⟩ 而非 |1⟩ 时触发——当某段子电路要在"没有激发"时才执行,这一招很好用:

# fire `kernel` when qubit0 is |1> AND qubit1 is |0>
qalgora.control(kernel, [qubit0, ~qubit1], kernel_arg)

这里的 ~qubit1 是负极性控制标记,非 Python 按位取反——它告诉合成器在 |0⟩ 时触发。

为何这很重要
正因为合成全由编译器掌控,受控内核是整体优化的,而非一门一门地处理——通常用到的双量子比特门,比你手动给每个操作逐个加控制要少得多。