Getting Started¶
This page covers installing Exaqt and driving the low-level ExaqtSV
state vector directly. To run mimiqcircuits.Circuit objects and collect
QCSResults, see Circuit Execution.
Requirements¶
- Python 3.10 or later
mimiqcircuits0.26.2 or later and NumPy 1.26 or later — both installed automatically with the wheel
Installation¶
Exaqt is distributed as mimiq-exaqt and imported as exaqt:
Pre-built wheels are published for Linux (x86-64), macOS (Apple silicon),
and Windows (x86-64). One abi3 wheel per platform covers Python 3.10 and
every later version, so no compiler or Rust toolchain is needed. On any
other platform, see Other platforms.
Verify the install:
import exaqt
print(exaqt.__version__)
print(exaqt.ExaqtSV.zero(2).amplitudes()) # [1+0j, 0j, 0j, 0j]
Other platforms¶
Need a platform without a published wheel — Linux aarch64 or Intel macOS, for example? Contact QPerfect.
Reading the documentation offline¶
The wheel ships this documentation site inside it, so the version you read always matches the version you installed:
exaqt docs # open the bundled site in a browser
exaqt docs --print-path # or print the path to its index.html
exaqt.docs_dir() returns the same directory from Python. The online copy
lives at https://docs.qperfect.io/exaqt-python/.
The state vector¶
ExaqtSV is the dense quantum register. Amplitudes are stored in
little-endian order: index i of the amplitude array is the basis state
whose bit k is set when qubit k is |1>. Qubit 0 is therefore the
least-significant bit.
import numpy as np
from exaqt import ExaqtSV
# Allocate |000> — three qubits, 2**3 = 8 amplitudes.
sv = ExaqtSV.zero(3)
print(sv.num_qubits) # 3
print(len(sv)) # 8
# Build a GHZ state: H on qubit 0, then fan out with CX.
sv.apply_h(0)
sv.apply_cx(0, 1)
sv.apply_cx(0, 2)
# Read the full amplitude vector (numpy complex128, length 2**3).
amps = sv.amplitudes()
print(amps[0], amps[7]) # (0.707..+0j) (0.707..+0j) — |000> and |111>
# Probability of a single basis state, without materialising the vector.
print(sv.probability(0)) # 0.5
print(sv.probability(7)) # 0.5
Gates¶
Common gates have dedicated methods; the target qubit is always the last positional argument, and rotation angles come first.
import math
from exaqt import ExaqtSV
sv = ExaqtSV.zero(2)
# Non-parametric single-qubit gates.
sv.apply_x(0) # Pauli-X on qubit 0
sv.apply_h(1) # Hadamard on qubit 1
# Parametric single-qubit gates: angle(s) first, target last.
sv.apply_rx(math.pi / 2, 0) # RX(pi/2) on qubit 0
sv.apply_u(math.pi, 0.0, math.pi, 1) # U(theta, phi, lambda) on qubit 1
# Two-qubit gates.
sv.apply_cx(0, 1) # control 0, target 1
sv.apply_rzz(math.pi / 4, 0, 1) # RZZ(pi/4) on qubits 0 and 1
For any unitary not covered by a named method, pass the matrix as a numpy
complex128 array:
import numpy as np
from exaqt import ExaqtSV
sv = ExaqtSV.zero(1)
# Hadamard as an explicit 2x2 matrix.
h = np.array([[1, 1], [1, -1]], dtype=np.complex128) / np.sqrt(2)
sv.apply_gate_1q(h, 0)
# A 4x4 array goes to apply_gate_2q(gate, q1, q2).
Expectation values¶
Compute <psi|O|psi> without mutating the state. Pass 1- or 2-qubit
operators as numpy matrices, or a Pauli string spanning any number of
qubits.
import numpy as np
from exaqt import ExaqtSV
sv = ExaqtSV.zero(3)
sv.apply_h(0)
sv.apply_cx(0, 1)
sv.apply_cx(0, 2) # GHZ state
# Pauli-string expectation — no need to build a 2**k x 2**k matrix.
# XXX is a GHZ stabiliser, so its expectation is +1.
print(sv.expectation_pauli("XXX", [0, 1, 2])) # (1+0j)
# 1-qubit operator expectation from a matrix.
z = np.array([[1, 0], [0, -1]], dtype=np.complex128)
print(sv.expectation_1q(z, 0)) # (0+0j) — <Z> = 0 on qubit 0
Measurement and sampling¶
Sampling and measurement draw from a seeded Rng. The same seed always
reproduces the same stream.
from exaqt import ExaqtSV, Rng
sv = ExaqtSV.zero(2)
sv.apply_h(0)
sv.apply_cx(0, 1) # Bell state
rng = Rng(seed=42)
# Draw 1000 shots at once — shape (1000, 2), dtype uint8, qubit 0 first.
shots = sv.sample(rng, nsamples=1000)
print(shots.shape) # (1000, 2)
# A single destructive measurement of one qubit (collapses the state).
outcome = sv.measure_qubit(0, rng)
print(outcome) # 0 or 1
Errors¶
The wrapper raises subclasses of ExaqtError:
GateShapeError— a gate matrix has the wrong shape or layout.QubitIndexError— a qubit index is out of bounds, or duplicated.DegenerateStateError— sampling a state with zero / non-finite norm.
A MemoryError is raised (instead of aborting the process) for
state-vector allocations that will not fit — for example
ExaqtSV.zero(40).
Next steps¶
- Circuit Execution — run whole
mimiqcircuitscircuits. - API Reference — the complete public surface.