Usage
Usage
Using Maestro
Maestro can be used in two ways:
- Python bindings — Direct access to the C++ simulation engine
- Cloud API — Via Composer Gateway
Python Bindings
QASM-based Execution
The simplest way to use Maestro is through the simple_execute function with an OpenQASM circuit:
import maestro
qasm_bell = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
result = maestro.simple_execute(qasm_bell, shots=1000)
print(result["counts"]) # e.g. {'00': 498, '11': 502}
print(result["simulator"]) # e.g. 'QCSim'
print(result["method"]) # e.g. 'Statevector'
print(f"Time: {result['time_taken']:.4f}s")QuantumCircuit Model
Maestro provides a native QuantumCircuit class for building circuits programmatically in Python, without needing QASM strings:
import maestro
QuantumCircuit = maestro.circuits.QuantumCircuit
# Create a Bell state
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.measure([(0, 0), (1, 1)]) # Map qubit 0 → classical bit 0, qubit 1 → bit 1
result = qc.execute(shots=1000)
print(result["counts"]) # {'00': ~500, '11': ~500}Available Gates
Single-qubit (non-parametric):
qc.x(0) # Pauli-X
qc.y(0) # Pauli-Y
qc.z(0) # Pauli-Z
qc.h(0) # Hadamard
qc.s(0) # S gate
qc.sdg(0) # S†
qc.t(0) # T gate
qc.tdg(0) # T†
qc.sx(0) # √XSingle-qubit (parametric):
qc.rx(0, 0.5) # Rx(θ)
qc.ry(0, 0.5) # Ry(θ)
qc.rz(0, 0.5) # Rz(θ)
qc.p(0, 0.5) # Phase gate
qc.u(0, 0.1, 0.2, 0.3) # U(θ, φ, λ)Two-qubit gates:
qc.cx(0, 1) # CNOT
qc.cy(0, 1) # Controlled-Y
qc.cz(0, 1) # Controlled-Z
qc.swap(0, 1) # SWAP
qc.cp(0, 1, 0.5) # Controlled-Phase
qc.crx(0, 1, 0.5) # Controlled-Rx
qc.cry(0, 1, 0.5) # Controlled-Ry
qc.crz(0, 1, 0.5) # Controlled-RzExecution with Backend Selection
# Automode — Maestro selects the best backend
result = qc.execute(shots=1000)
# Explicit backend selection
result = qc.execute(
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.Statevector,
shots=1000,
)
# MPS simulation with tunable bond dimension
result = qc.execute(
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.MatrixProductState,
max_bond_dimension=64,
singular_value_threshold=1e-6,
shots=1000,
)Expectation Value Estimation
Estimate expectation values without sampling, using exact state vector computation:
qc = QuantumCircuit()
qc.x(0)
# Single observable
result = qc.estimate(observables="Z")
print(result["expectation_values"]) # [-1.0]
# Multiple observables (semicolon-separated or list)
result = qc.estimate(observables="Z;I")
print(result["expectation_values"]) # [-1.0, 1.0]
# Or as a list
result = qc.estimate(observables=["X", "Z"])
print(result["expectation_values"]) # [0.0, -1.0]Multi-qubit observables on entangled states:
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
# Bell state: (|00⟩ + |11⟩) / √2
result = qc.estimate(observables=["XX", "ZZ", "IZ"])
# XX → 1.0, ZZ → 1.0, IZ → 0.0Estimate with MPS backend:
result = qc.estimate(
observables="Z",
simulator_type=maestro.SimulatorType.QCSim,
simulation_type=maestro.SimulationType.MatrixProductState,
max_bond_dimension=4,
)Simulation Types
| Type | Enum Value | Best For |
|---|---|---|
| State Vector | SimulationType.Statevector |
Exact simulation, ≤30 qubits |
| MPS | SimulationType.MatrixProductState |
Large, low-entanglement circuits |
| Stabilizer | SimulationType.Stabilizer |
Clifford-only circuits (any size) |
| Pauli Propagator | SimulationType.PauliPropagator |
Pauli-heavy circuits |
| Extended Stabilizer | SimulationType.ExtendedStabilizer |
Near-Clifford circuits |
| Auto | SimulationType.Auto |
Let Maestro choose the optimal backend |
Low-Level API
For fine-grained control, you can manage simulators directly:
import maestro
m = maestro.Maestro()
# Create a simulator
handle = m.create_simulator(
maestro.SimulatorType.QCSim,
maestro.SimulationType.Statevector,
)
simulator = m.get_simulator(handle)
# Allocate and initialize
simulator.AllocateQubits(2)
simulator.Initialize()
# Apply gates directly
simulator.ApplyH(0)
simulator.ApplyCX(0, 1)
# Sample
results = simulator.SampleCounts([0, 1], 1000)
print(results) # {'00': ~500, '11': ~500}
# Clean up
m.destroy_simulator(handle)