|
Maestro 0.1.0
Unified interface for quantum circuit simulation
|
This tutorial demonstrates how to use the Maestro library to simulate quantum circuits in C++.
Maestro provides a unified C interface to various quantum simulation backends. You can define circuits using QASM or a JSON format and execute them on the most appropriate simulator.
The core workflow involves:
Below is a complete C++ example. You can also find this in examples/basic_simulation.cpp.
To compile this example, you need to link against the maestro library.
Assuming you have built Maestro in the build directory:
*Note: You may need to adjust include paths (-I) depending on where maestrolib/Interface.h is located relative to your source file.*
Instead of SimpleExecute, you can manually control the simulator state. See examples/advanced_simulation.cpp for a complete runnable example.
The jsonConfig string in SimpleExecute supports various keys:
shots: Number of execution shots (integer).matrix_product_state_max_bond_dimension: Max bond dimension for MPS (string/int).matrix_product_state_truncation_threshold: Truncation threshold for MPS (string/double).mps_sample_measure_algorithm: Algorithm for MPS sampling (string).Maestro allows calculating the expectation values of observables without needing to perform manual sampling. This is particularly useful for Variational Quantum Algorithms.
Maestro provides Python bindings for ease of use, allowing you to integrate its high-performance simulation capabilities into your Python-based quantum workflows.
To install the Python bindings, run the following command from the root of the Maestro repository:
This will compile the C++ core and install the maestro Python package.
The easiest way to use Maestro in Python is through the simple_execute and simple_estimate functions.
The QuantumCircuit class provides a Pythonic, object-oriented API for building and executing quantum circuits directly in Python — no QASM strings required. This is the recommended approach for most Python workflows.
The QuantumCircuit supports a comprehensive gate set. Qubits are referenced by integer index (0-based) and are automatically allocated as needed.
Single-Qubit Gates (Non-Parametric)
| Method | Gate | Description |
|---|---|---|
qc.x(q) | Pauli-X | Bit flip |
qc.y(q) | Pauli-Y | Bit + phase flip |
qc.z(q) | Pauli-Z | Phase flip |
qc.h(q) | Hadamard | Creates superposition |
qc.s(q) | S Gate | π/2 phase |
qc.sdg(q) | S† Gate | −π/2 phase |
qc.t(q) | T Gate | π/4 phase |
qc.tdg(q) | T† Gate | −π/4 phase |
qc.sx(q) | √X Gate | Square root of X |
Single-Qubit Gates (Parametric)
| Method | Gate | Parameters |
|---|---|---|
qc.p(q, λ) | Phase | λ (lambda) |
qc.rx(q, θ) | Rx | θ (theta) |
qc.ry(q, θ) | Ry | θ (theta) |
qc.rz(q, θ) | Rz | θ (theta) |
qc.u(q, θ, φ, λ) | U | θ, φ, λ |
Two-Qubit Gates
| Method | Gate | Description |
|---|---|---|
qc.cx(c, t) | CNOT | Controlled-X (control, target) |
qc.cy(c, t) | CY | Controlled-Y |
qc.cz(c, t) | CZ | Controlled-Z |
qc.swap(a, b) | SWAP | Swaps two qubits |
Controlled Parametric Gates
| Method | Gate | Parameters |
|---|---|---|
qc.cp(c, t, λ) | CP | λ (lambda) |
qc.crx(c, t, θ) | CRx | θ (theta) |
qc.cry(c, t, θ) | CRy | θ (theta) |
qc.crz(c, t, θ) | CRz | θ (theta) |
Add measurements by providing a list of (qubit_index, classical_bit_index) pairs:
You can also remap the classical bit assignments. For example, to store the result of qubit 0 in classical bit 1 and vice versa:
The execute method runs the circuit for a given number of shots and returns a dictionary with measurement counts:
Execution Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
simulator_type | enum | SimulatorType.QCSim | Simulator backend |
simulation_type | enum | SimulationType.Statevector | Simulation method |
shots | int | 1024 | Number of measurement shots |
max_bond_dimension | int | 2 | Max bond dimension (MPS only) |
singular_value_threshold | float | 1e-8 | Truncation threshold (MPS only) |
Example with MPS backend:
The estimate method computes expectation values of Pauli observables without sampling. This is ideal for variational algorithms and Hamiltonian simulation.
Observables can be specified as either a semicolon-separated string or a list of strings. Each observable is a Pauli string (e.g., "ZZ", "XII", "IYZ").
Estimation Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
observables | str or list | *(required)* | Pauli observables to measure |
simulator_type | enum | SimulatorType.QCSim | Simulator backend |
simulation_type | enum | SimulationType.Statevector | Simulation method |
max_bond_dimension | int | 2 | Max bond dimension (MPS only) |
singular_value_threshold | float | 1e-8 | Truncation threshold (MPS only) |
Note: The
estimatemethod does not require measurements to be added to the circuit. The number of qubits is automatically inferred from the circuit operations and observable lengths.
For more granular control, you can use the Maestro and ISimulator classes directly.
You can find several complete examples in the examples/ directory:
examples/python_example_1.py: Basic simulation and sampling.examples/python_example_2.py: Advanced simulation with manual gate application.examples/python_example_3.py: Working with expectation values and observables.