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〉.
子电路合成 — control 与 adjoint
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〉 时触发。