Circuit Transformations
copy(), inverse(), dagger(), reverse(), repeat(), decompose(), and control() return a new circuit. append(), prepend(), initialize(), and gate methods mutate the circuit in place.
Distinguish these behaviors before choosing a method: transformations that return a circuit leave the original object unchanged, while composition and initialization methods modify the receiver.
from unitarylab import Circuit
qc = Circuit(2)
qc.h(0)
qc.cx(0, 1)
qc_copy = qc.copy()
qc_inverse = qc.inverse()
qc_dagger = qc.dagger()
qc_reversed = qc.reverse()
qc_repeated = qc.repeat(3)
qc_decomposed = qc.decompose(n=1)
qc_controlled = qc.control(num_control_qubits=2, control_state="01")| Method | Description |
|---|---|
copy() | Return an independent copy |
inverse() / dagger() | Reverse gate order and take each gate’s conjugate transpose |
reverse() | Reverse qubit indices without changing gate order |
repeat(times) | Repeat the complete circuit |
decompose(n, name) | Expand block gates to the requested depth |
control(num_control_qubits, control_state=None) | Wrap the circuit in a controlled circuit |
append(other, target, ...) | Append a subcircuit in place |
prepend(other, target, ...) | Prepend a subcircuit in place |
initialize(state, target, ...) | Prepare a state in place; target qubits must not have been used |
inverse() and dagger() are equivalent and produce the circuit adjoint. A circuit containing non-unitary operations such as measurement cannot be inverted. reverse() remaps qubit positions only; it does not reverse the time order of gates.
import numpy as np
from unitarylab import Circuit
sub = Circuit(2)
sub.z(0)
appended = qc.copy()
appended.append(sub, target=[0, 1])
prepared = Circuit(2)
state = np.array([1 / np.sqrt(2), 1 / np.sqrt(2)])
prepared.initialize(state, target=0)
prepared.cx(0, 1)The executor expands block-gate contents before execution. To draw or analyze the internal structure of block gates, call decompose() first.
Composition constraints
- The length of
targetinappend()andprepend()must match the subcircuit width; target indices must be unique. - Optional control qubits cannot overlap with
target; the length ofcontrol_statemust match the number of controls. repeat(times)requires a positive integertimes.initialize()requires a state vector of length , wherekis the number of target qubits; the state must be normalizable.decompose(n=...)expands only the requested number of levels; repeated calls or largerncan expand nested blocks further.
Verifying transformations
For a small circuit without measurements, compare matrices to verify composition:
import numpy as np
identity = qc.copy()
identity.append(qc.inverse(), target=[0, 1])
assert np.allclose(identity.get_matrix(), np.eye(4))Matrix construction requires memory exponential in the qubit count and is suitable only for small-circuit checks.