Overview
Maestro ships high-performance Python bindings built with nanobind. The bindings expose the full simulation pipeline — circuit construction, backend selection, execution, and expectation-value estimation — in a Pythonic API.
Installation
Install pre-built wheels from PyPI (Linux, macOS, Windows):
Or build from source from the repository root:
Supported platforms (pre-built wheels):
| Platform | Architecture | Python |
| Linux | x86_64 | 3.10, 3.11, 3.12 |
| macOS | arm64 (Apple Silicon) | 3.10, 3.11, 3.12 |
| Windows | AMD64 | 3.10, 3.11, 3.12 |
That's it — you're ready to import maestro and run quantum simulations.
Quick Start — One-Line Execution
The fastest way to run a circuit is maestro.simple_execute. Pass an OpenQASM 2.0 string and get measurement counts back immediately.
import maestro
qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q -> c;
"""
result = maestro.simple_execute(qasm, shots=1024)
print(result["counts"])
print(result["simulator"])
print(result["method"])
print(f"{result['time_taken']:.4f}s")
What's in the result dict?
| Key | Type | Description |
counts | dict[str, int] | Measurement outcome → count |
simulator | int | Backend enum value (see SimulatorType) |
method | int | Simulation method enum value (see SimulationType) |
time_taken | float | Wall-clock time in seconds |
Choosing a Simulation Backend
You can explicitly select the simulator type and simulation method. Maestro supports several backends; the most common CPU options are shown here.
Statevector Simulation
result = maestro.simple_execute(
qasm,
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.Statevector,
shots=2000
)
Matrix Product State (MPS)
MPS enables simulation of circuits with hundreds of qubits when entanglement is bounded.
result = maestro.simple_execute(
qasm,
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.MatrixProductState,
max_bond_dimension=64,
singular_value_threshold=1e-10,
shots=1000
)
print(result["method"])
Stabilizer (Clifford-only)
Ultra-fast simulation for circuits that only use Clifford gates (H, S, CX, X, Y, Z).
clifford_qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[100];
creg c[100];
h q[0];
cx q[0], q[1];
cx q[1], q[2];
measure q -> c;
"""
result = maestro.simple_execute(
clifford_qasm,
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.Stabilizer,
shots=10000
)
Available Backends
The table below shows which simulation types are supported by each backend.
| SimulationType | QCSim | Qiskit Aer | GPU | QuEST |
Statevector | ✅ | ✅ | ✅ | ✅ |
MatrixProductState | ✅ | ✅ | ✅ | ❌ |
Stabilizer | ✅ | ✅ | ❌ | ❌ |
TensorNetwork | ✅ | ✅ | ✅ | ❌ |
PauliPropagator | ✅ | ❌ | ✅ | ❌ |
ExtendedStabilizer | ❌ | ✅ | ❌ | ❌ |
Distributed execution: QCSim and Qiskit Aer support p-block composite simulation via CompositeQCSim / CompositeQiskitAer. QuEST natively supports MPI-distributed statevector simulation.
- Note
- Qiskit Aer support is optional and requires building with
AER_INCLUDE_DIR. GPU and QuEST are dynamically-loaded libraries — see the dedicated sections below.
Expectation Values
Use maestro.simple_estimate to compute expectation values of Pauli observables without measurements in the circuit.
import maestro
ghz = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[3];
h q[0];
cx q[0], q[1];
cx q[1], q[2];
"""
result = maestro.simple_estimate(ghz, "ZZZ;XXX;IZI")
obs = result["expectation_values"]
print(f"<ZZZ> = {obs[0]:.4f}")
print(f"<XXX> = {obs[1]:.4f}")
print(f"<IZI> = {obs[2]:.4f}")
You can also use a specific backend:
result = maestro.simple_estimate(
ghz, "ZZ;XX",
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.MatrixProductState,
max_bond_dimension=8
)
QuantumCircuit Builder
For a more Pythonic workflow (similar to Qiskit), use the QuantumCircuit class to build circuits programmatically without writing QASM strings.
Building and Running a Circuit
from maestro.circuits import QuantumCircuit
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.measure([(0, 0), (1, 1)])
result = qc.execute(shots=1000)
print(result["counts"])
Available Gates
| Gate | Method | Parameters |
| Pauli-X | qc.x(qubit) | — |
| Pauli-Y | qc.y(qubit) | — |
| Pauli-Z | qc.z(qubit) | — |
| Hadamard | qc.h(qubit) | — |
| S | qc.s(qubit) | — |
| S† | qc.sdg(qubit) | — |
| T | qc.t(qubit) | — |
| T† | qc.tdg(qubit) | — |
| √X | qc.sx(qubit) | — |
| √X† | qc.sxdg(qubit) | — |
| K | qc.k(qubit) | — |
| Phase | qc.p(qubit, λ) | λ (radians) |
| Rx | qc.rx(qubit, θ) | θ (radians) |
| Ry | qc.ry(qubit, θ) | θ (radians) |
| Rz | qc.rz(qubit, θ) | θ (radians) |
| U | qc.u(qubit, θ, φ, λ) | 3 angles |
| CNOT | qc.cx(ctrl, tgt) | — |
| CY | qc.cy(ctrl, tgt) | — |
| CZ | qc.cz(ctrl, tgt) | — |
| CH | qc.ch(ctrl, tgt) | — |
| CSX | qc.csx(ctrl, tgt) | — |
| CSX† | qc.csxdg(ctrl, tgt) | — |
| SWAP | qc.swap(q1, q2) | — |
| CP | qc.cp(ctrl, tgt, λ) | λ |
| CRx | qc.crx(ctrl, tgt, θ) | θ |
| CRy | qc.cry(ctrl, tgt, θ) | θ |
| CRz | qc.crz(ctrl, tgt, θ) | θ |
| CU | qc.cu(ctrl, tgt, θ, φ, λ, γ) | 4 angles |
| Toffoli | qc.ccx(c1, c2, tgt) | — |
| Fredkin | qc.cswap(ctrl, q1, q2) | — |
Measurements
qc.measure([(0, 0), (1, 1), (2, 2)])
qc.measure_all()
Expectation Values with QuantumCircuit
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
result = qc.estimate(
observables=["ZZ", "XX", "YY"],
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.Statevector
)
vals = result["expectation_values"]
print(f"<ZZ> = {vals[0]:.4f}")
print(f"<XX> = {vals[1]:.4f}")
print(f"<YY> = {vals[2]:.4f}")
Mirror Fidelity
Mirror fidelity measures how well a circuit "undoes itself". Maestro constructs the mirror circuit by appending the adjoint (inverse) of every gate in reverse order and returns P(|0…0⟩) — the probability of returning to the initial state. A value of 1.0 indicates a perfect simulation.
This is useful for benchmarking simulator accuracy, characterising noise from approximate methods (e.g. MPS with low bond dimension), and for circuit validation.
By default, mirror fidelity uses shot-based sampling (1024 shots). For exact results on small circuits, pass full_amplitude=True.
Module-Level Function
import maestro
from maestro.circuits import QuantumCircuit
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.rx(0, 3.14159 / 4)
fidelity = maestro.mirror_fidelity(qc)
print(f"Mirror fidelity: {fidelity:.4f}")
fidelity = maestro.mirror_fidelity(qc, shots=10000)
fidelity = maestro.mirror_fidelity(qc, full_amplitude=True)
Circuit Method
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.s(0)
fidelity = qc.mirror_fidelity()
print(f"Mirror fidelity: {fidelity:.4f}")
fidelity = qc.mirror_fidelity(
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.MatrixProductState,
shots=10000,
max_bond_dimension=64,
)
fidelity = qc.mirror_fidelity(full_amplitude=True)
- Note
- Measurements in the original circuit are automatically skipped when building the mirror circuit — only unitary gate operations are mirrored. All gate types are supported: self-inverse gates (H, X, CX, etc.), paired gates (S↔S†, T↔T†, √X↔√X†), and parametric gates (Rx, Ry, Rz, U, CP, CRx, CRy, CRz, CU).
Inner Product
The inner product computes ⟨ψ₁|ψ₂⟩ between two circuits' output states, where |ψᵢ⟩ = Uᵢ|0⟩. Internally, Maestro builds the combined circuit U₂ followed by U₁† and evaluates ⟨0|U₁†U₂|0⟩ via the efficient ProjectOnZero operation — this is particularly fast with the MPS backend as it avoids constructing the full statevector.
The result is a complex number: magnitude gives the overlap (fidelity when squared), and phase captures relative phase information.
Module-Level Function
import maestro
from maestro.circuits import QuantumCircuit
qc1 = QuantumCircuit()
qc1.h(0)
qc1.cx(0, 1)
qc2 = QuantumCircuit()
qc2.h(0)
qc2.cx(0, 1)
overlap = maestro.inner_product(qc1, qc2)
print(f"<psi1|psi2> = {overlap}")
print(f"|<psi1|psi2>| = {abs(overlap)}")
Orthogonal States
qc_plus = QuantumCircuit()
qc_plus.h(0)
qc_minus = QuantumCircuit()
qc_minus.x(0)
qc_minus.h(0)
overlap = maestro.inner_product(qc_plus, qc_minus)
print(f"|<+|->| = {abs(overlap):.10f}")
Circuit Method
qc1 = QuantumCircuit()
qc1.h(0)
qc1.cx(0, 1)
qc2 = QuantumCircuit()
qc2.h(0)
qc2.cx(0, 1)
overlap = qc1.inner_product(qc2)
print(f"|overlap| = {abs(overlap):.4f}")
overlap = qc1.inner_product(
qc2,
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.MatrixProductState,
max_bond_dimension=64,
)
Parameters
| Parameter | Type | Default | Description |
circuit_1 / self | QuantumCircuit | — | First circuit (bra state) |
circuit_2 / other | QuantumCircuit | — | Second circuit (ket state) |
simulator_type | SimulatorType | QCSim | Simulation backend |
simulation_type | SimulationType | Statevector | Simulation method |
max_bond_dimension | int or None | None | MPS bond dimension limit |
singular_value_threshold | float or None | None | MPS truncation threshold |
use_double_precision | bool | False | GPU double precision flag |
- Note
- Measurements in either circuit are automatically skipped — only unitary gate operations contribute to the inner product.
Advanced: Object-Oriented Simulator Control
For full control over the simulation lifecycle, use the Maestro class directly to create, configure, and destroy simulators.
import maestro
m = maestro.Maestro()
handle = m.create_simulator(
maestro.SimulatorType.QCSim,
maestro.SimulationType.Statevector
)
sim = m.get_simulator(handle)
m.destroy_simulator(handle)
- Note
- The
Maestro class is primarily used for handle-based lifecycle management. For most Python workflows, the QuantumCircuit API or the simple_execute / simple_estimate convenience functions are recommended.
QuEST Backend
Maestro supports the QuEST simulator as an alternative CPU backend. QuEST is loaded dynamically as a shared library (libmaestroquest), so it is available only when the library has been built and installed.
QuEST natively supports MPI-distributed statevector simulation, enabling execution across multiple nodes for larger qubit counts.
Checking Availability
import maestro
if maestro.is_quest_available():
print("QuEST is available")
else:
print("QuEST not found — install libmaestroquest")
maestro.init_quest()
Running a Circuit on QuEST
QuEST only supports Statevector simulation. Requesting any other simulation type (MPS, Stabilizer, etc.) will raise an error.
qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q -> c;
"""
result = maestro.simple_execute(
qasm,
simulator_type=maestro.SimulatorType.QuestSim,
simulation_type=maestro.SimulationType.Statevector,
shots=1000
)
print(result["counts"])
Expectation Values with QuEST
bell = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
cx q[0], q[1];
"""
result = maestro.simple_estimate(
bell, "ZZ;XX;YY",
simulator_type=maestro.SimulatorType.QuestSim,
simulation_type=maestro.SimulationType.Statevector
)
vals = result["expectation_values"]
print(f"<ZZ> = {vals[0]:.4f}")
print(f"<XX> = {vals[1]:.4f}")
print(f"<YY> = {vals[2]:.4f}")
QuantumCircuit with QuEST
The programmatic QuantumCircuit API works with QuEST too:
from maestro.circuits import QuantumCircuit
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
result = qc.execute(
simulator_type=maestro.SimulatorType.QuestSim,
simulation_type=maestro.SimulationType.Statevector,
shots=100
)
print(result["counts"])
- Note
- QuEST only supports
SimulationType.Statevector. Passing MatrixProductState, Stabilizer, TensorNetwork, or PauliPropagator will raise an exception: "QuestSim only supports Statevector".
GPU Backend
Maestro supports GPU-accelerated simulation via a dynamically-loaded CUDA library (libmaestro_gpu_simulators). Like QuEST, the GPU backend is optional and loaded at runtime only when requested.
- Note
- The GPU backend is not included in the open-source version of Maestro. Contact Qoro Quantum for access.
Unlike QuEST, the GPU backend supports multiple simulation types including Statevector, MPS, Tensor Network, and Pauli Propagation.
Checking Availability
import maestro
if maestro.is_gpu_available():
print("GPU backend is available")
else:
print("GPU not found — install libmaestro_gpu_simulators")
maestro.init_gpu()
Running a Circuit on GPU
import maestro
maestro.init_gpu()
qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q -> c;
"""
result = maestro.simple_execute(
qasm,
simulator_type=maestro.SimulatorType.Gpu,
simulation_type=maestro.SimulationType.Statevector,
shots=1000
)
print(result["counts"])
result_mps = maestro.simple_execute(
qasm,
simulator_type=maestro.SimulatorType.Gpu,
simulation_type=maestro.SimulationType.MatrixProductState,
max_bond_dimension=64,
shots=1000
)
print(result_mps["counts"])
Expectation Values on GPU
bell = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
cx q[0], q[1];
"""
result = maestro.simple_estimate(
bell, "ZZ;XX;YY",
simulator_type=maestro.SimulatorType.Gpu,
simulation_type=maestro.SimulationType.Statevector
)
vals = result["expectation_values"]
print(f"<ZZ> = {vals[0]:.4f}")
print(f"<XX> = {vals[1]:.4f}")
print(f"<YY> = {vals[2]:.4f}")
QuantumCircuit with GPU
from maestro.circuits import QuantumCircuit
maestro.init_gpu()
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
result = qc.execute(
simulator_type=maestro.SimulatorType.Gpu,
simulation_type=maestro.SimulationType.Statevector,
shots=2048
)
print(result["counts"])
Supported Simulation Types
| SimulationType | GPU Support |
Statevector | ✅ |
MatrixProductState | ✅ |
TensorNetwork | ✅ |
PauliPropagator | ✅ |
Stabilizer | ❌ |
ExtendedStabilizer | ❌ |
Fallback Pattern
For portable scripts that should work with or without GPU:
import maestro
if maestro.init_gpu():
sim_type = maestro.SimulatorType.Gpu
print("Using GPU")
elif maestro.init_quest():
sim_type = maestro.SimulatorType.QuestSim
print("Using QuEST")
else:
sim_type = maestro.SimulatorType.QCSim
print("Using CPU (QCSim)")
result = maestro.simple_execute(qasm, simulator_type=sim_type, shots=1000)
print(result["counts"])
Enumerations Reference
SimulatorType
| Value | Description | Dynamic? |
SimulatorType.QCSim | QCSim CPU simulator | No (built-in) |
SimulatorType.CompositeQCSim | Composite (distributed) CPU simulator | No (built-in) |
SimulatorType.QuestSim | QuEST simulator | Yes (libmaestroquest) |
SimulatorType.Gpu | GPU-accelerated simulator (requires CUDA) | Yes (libmaestro_gpu_simulators) |
SimulationType
| Value | Description | Supported By |
SimulationType.Statevector | Full statevector simulation | QCSim, Aer, GPU, QuEST |
SimulationType.MatrixProductState | MPS / tensor-train simulation | QCSim, Aer, GPU |
SimulationType.Stabilizer | Clifford stabiliser simulation | QCSim, Aer |
SimulationType.TensorNetwork | Tensor network contraction | QCSim, Aer, GPU |
SimulationType.PauliPropagator | Pauli frame propagation | QCSim, GPU |
SimulationType.ExtendedStabilizer | Extended stabiliser decomposition | Aer only |