Circuits and gates¶
Building circuits¶
Circuit stores a list of operations. Gates are
attributes; qubits are selected with [...]; parametric gates take their
parameters as a call before the qubit indexing. Everything chains:
import math
from blueqat import Circuit
Circuit().h[0].cx[0, 1].rz(math.pi / 4)[1].m[:]
Qubit 0 is always the least-significant bit of the statevector index
('10' means qubit 1 is 1, qubit 0 is 0 – the same convention as Qiskit’s
Statevector).
Gate set¶
- Single-qubit gates
i,x,y,z,h,s,sdg,t,tdg,sx,sxdg,phase(theta)(aliasesp,r),rx(theta),ry(theta),rz(theta),u(theta, phi, lam[, gamma]),mat1(matrix)(arbitrary 2x2 unitary).- Two-qubit gates
cx(aliascnot),cy,cz,ch,swap,iswap,iswapdg,cphase(theta)(aliasescp,cr),crx,cry,crz,cu(theta, phi, lam[, gamma]),rxx(theta),ryy(theta),rzz(theta),zz,zzdg,exch(theta)(Heisenberg exchange pulse, see Exchange-only spin qubits).- Three-qubit gates
ccx(aliastoffoli),ccz,cswap.- Other operations
m/measure(optionallym(key="name")for keyed mid-circuit measurement),reset,barrier.
Gates that take no parameters raise ValueError if parameters are passed
(e.g. x(0.5)[0] is rejected rather than silently ignored).
Introspection¶
c = Circuit(3).h[:].cx[0, 1].cx[1, 2].m[:]
c.n_qubits # 3
c.depth() # 4 (parallel gates count once; barriers don't count)
c.count_ops() # Counter({'h': 3, 'cx': 2, 'measure': 3})
Measurement probabilities (differentiable, optionally marginalized onto selected qubits) and Hamiltonian expectation values:
from blueqat.utils import Z
Circuit(2).h[0].cx[0, 1].probs() # tensor([0.5, 0., 0., 0.5])
Circuit(2).h[0].cx[0, 1].probs([1]) # marginal of qubit 1
Circuit(1).rx(0.4)[0].expect(1.0 * Z[0]) # <Z> = cos(0.4)
Inverse circuits¶
dagger() returns the Hermitian conjugate
(gates reversed and conjugated). Measurement and reset have no inverse;
dagger(ignore_measurement=True) drops them instead of raising:
c = Circuit(3) # ... build ...
identity = c + c.dagger() # uncomputes back to |0...0>
OpenQASM 2.0¶
qasm = Circuit(2).h[0].cx[0, 1].to_qasm()
from blueqat.circuit_funcs import from_qasm
c = from_qasm(qasm)
JSON serialization¶
Circuits round-trip through a versioned, JSON-compatible schema (this is also the cloud submission wire format):
from blueqat.circuit_funcs.json_serializer import serialize, deserialize
data = serialize(Circuit(2).h[0].cx[0, 1])
c = deserialize(data)
Drawing¶
run(backend='draw') renders the circuit with matplotlib. Every registered
gate is drawable; unknown (user-registered) gates are omitted with a
UserWarning.
Named gate blocks¶
Real algorithms are nests of subroutines – Shor’s order finding is initialization, controlled modular multiplications and an inverse QFT, each built from smaller pieces. Named blocks keep that structure in the circuit object without changing execution (every backend transparently sees the inner gates):
c = Circuit(7)
with c.block("order-finding"):
with c.block("superposition"):
c.h[4, 5, 6]
with c.block("c-U^1"):
c.cswap[4, 2, 3].cswap[4, 1, 2].cswap[4, 0, 1]
# place a library circuit as a block, shifted to qubits 4..6
c.append_block("IQFT", qft_circuit(3).dagger(), offset=4)
print(c.tree())
# Circuit(7)
# └─ order-finding
# ├─ superposition
# │ └─ h[4, 5, 6]
# ├─ c-U^1
# │ └─ ...
# └─ IQFT
# └─ ...
Blocks nest arbitrarily, show up in repr() and tree(),
and survive dagger() as mirrored blocks
("order-finding†"). depth() / count_ops() count the contained
gates; flatten() / JSON serialization expand blocks into plain gates
(the flat wire format keeps no hierarchy). The circuit drawer renders a
block as a single labeled box spanning its qubits; a circuit wrapped
entirely in one block automatically descends so its child blocks appear as
boxes. Pass expand_blocks=n to open n levels of blocks, or
expand_blocks=True to draw every inner gate. See
examples/shor_15.py for a complete Shor-at-15 program written this way.
Ancilla qubits¶
c = Circuit(4).h[:]
with c.ancilla() as a: # allocates a fresh qubit
c.cx[0, a[0]]
c.cx[0, a[0]]
# the ancilla is reset to |0> on exit (reset=True by default)
Macros and custom gates¶
Register a function as a circuit method, or a gate class into the gate set:
from blueqat import BlueqatGlobalSetting
from blueqat.decorators import circuitmacro
@circuitmacro
def bell(c, a, b):
return c.h[a].cx[a, b]
Circuit(2).bell(0, 1)
BlueqatGlobalSetting.register_gate('mygate', MyGateClass)