qsengine 1.1.1

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

GateMethodWhat it does
Hadamardqc.h(q)Puts a basis state into an equal superposition: ∣0⟩\ket{0} becomes (∣0⟩+∣1⟩)/2(\ket{0} + \ket{1})/\sqrt{2}.
Pauli Xqc.x(q)Bit flip, the quantum NOT: ∣0⟩\ket{0} and ∣1⟩\ket{1} swap.
Pauli Yqc.y(q)Bit flip and phase flip: ∣0⟩\ket{0} becomes i∣1⟩i\ket{1}.
Pauli Zqc.z(q)Phase flip: ∣1⟩\ket{1} becomes −∣1⟩-\ket{1}.
Sqc.s(q)Quarter turn of phase: ∣1⟩\ket{1} becomes i∣1⟩i\ket{1}.
S daggerqc.sdg(q)Undoes S: ∣1⟩\ket{1} becomes −i∣1⟩-i\ket{1}.
Tqc.t(q)Eighth turn of phase: ∣1⟩\ket{1} becomes eiπ/4∣1⟩e^{i\pi/4}\ket{1}.
T daggerqc.tdg(q)Undoes T: ∣1⟩\ket{1} becomes e−iπ/4∣1⟩e^{-i\pi/4}\ket{1}.
Phaseqc.p(q, lam)Any turn of phase: ∣1⟩\ket{1} becomes eiλ∣1⟩e^{i\lambda}\ket{1}. 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.
Uqc.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:

U(θ,φ,λ)=(cos⁡θ2−eiλsin⁡θ2eiφsin⁡θ2ei(φ+λ)cos⁡θ2)U(\theta, \varphi, \lambda) = \begin{pmatrix} \cos\tfrac{\theta}{2} & -e^{i\lambda}\sin\tfrac{\theta}{2} \\ e^{i\varphi}\sin\tfrac{\theta}{2} & e^{i(\varphi+\lambda)}\cos\tfrac{\theta}{2} \end{pmatrix}

Gates on two and three qubits

GateMethodWhat it does
CNOTqc.cx(control, target)Flips the target when the control is ∣1⟩\ket{1}.
Controlled Zqc.cz(control, target)Flips the phase of ∣11⟩\ket{11}. Either qubit can be the control.
Controlled phaseqc.cp(control, target, lam)Multiplies ∣11⟩\ket{11} by eiλe^{i\lambda}. Either qubit can be the control.
Controlled RZqc.crz(control, target, theta)Rotates the target about Z by θ when the control is ∣1⟩\ket{1}.
SWAPqc.swap(qubit1, qubit2)Exchanges the states of two qubits.
Toffoliqc.ccx(control1, control2, target)Flips the target when both controls are ∣1⟩\ket{1}.

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 ∣1⟩\ket{1}. 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) raises ValueError.
  • 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.