Gates
qsengine has nineteen named gates, and unitary() for any other. Each is a method on QuantumCircuit that records the gate; the circuit runs only when you ask for a result. q, control and target are qubit numbers starting at 0, and angles are in radians.
Single-qubit gates
| Gate | Method | What it does |
|---|---|---|
| Hadamard | qc.h(q) | Puts a basis state into an equal superposition: becomes . |
| Pauli X | qc.x(q) | Bit flip, the quantum NOT: and swap. |
| Pauli Y | qc.y(q) | Bit flip and phase flip: becomes . |
| Pauli Z | qc.z(q) | Phase flip: becomes . |
| S | qc.s(q) | Quarter turn of phase: becomes . |
| S dagger | qc.sdg(q) | Undoes S: becomes . |
| T | qc.t(q) | Eighth turn of phase: becomes . |
| T dagger | qc.tdg(q) | Undoes T: becomes . |
| Phase | qc.p(q, lam) | Any turn of phase: becomes . p(q, math.pi) is Z. |
| RX(θ) | qc.rx(q, theta) | Rotation by θ radians about the X axis of the Bloch sphere. |
| RY(θ) | qc.ry(q, theta) | Rotation by θ radians about the Y axis. |
| RZ(θ) | qc.rz(q, theta) | Rotation by θ radians about the Z axis. |
| U | qc.u(q, theta, phi, lam) | Any single-qubit gate, from three angles, in the same convention as Qiskit. |
U is the general case. Every other gate in this table is U with particular angles, up to an overall phase:
Gates on two and three qubits
| Gate | Method | What it does |
|---|---|---|
| CNOT | qc.cx(control, target) | Flips the target when the control is . |
| Controlled Z | qc.cz(control, target) | Flips the phase of . Either qubit can be the control. |
| Controlled phase | qc.cp(control, target, lam) | Multiplies by . Either qubit can be the control. |
| Controlled RZ | qc.crz(control, target, theta) | Rotates the target about Z by θ when the control is . |
| SWAP | qc.swap(qubit1, qubit2) | Exchanges the states of two qubits. |
| Toffoli | qc.ccx(control1, control2, target) | Flips the target when both controls are . |
SWAP is recorded as three CNOTs, so it adds 3 to len(qc). Here it moves a 1 from qubit 0 to qubit 2:
qc = qsengine.QuantumCircuit(3)
qc.x(0)
qc.swap(0, 2)
print(qc.run(shots=100, seed=0))Output
{'100': 100}Your own gates
qc.unitary(matrix, target, controls=[]) applies any 2 by 2 unitary matrix, given as nested lists or a NumPy array. With controls, it applies only where every control qubit is . This applies the square root of X twice, which makes a full X:
import numpy as np
import qsengine
sx = np.array([[1 + 1j, 1 - 1j], [1 - 1j, 1 + 1j]]) / 2
qc = qsengine.QuantumCircuit(1)
qc.unitary(sx, 0)
qc.unitary(sx, 0)
print(qc.probabilities())Output
[0. 1.]A matrix that is not unitary raises ValueError, so a typo cannot quietly break the state. A matrix that is unitary to within 1e-9, such as one computed with floating-point arithmetic, is accepted and replaced by the nearest exactly unitary matrix, so small rounding errors cannot add up over a long circuit.
Rules every gate follows
- A qubit number must be at least 0 and less than the circuit’s
num_qubits. - A gate on several qubits needs different qubits:
qc.cx(1, 1)raisesValueError. - A gate that raises is not recorded, so the circuit stays as it was.
Combining gates
Gates apply in the order you add them. These lines make a three-qubit GHZ state:
qc = qsengine.QuantumCircuit(3)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)Runs of single-qubit gates on the same qubit are merged into one before the simulation runs, so a long chain of rotations costs one pass over the state, not one per gate.