Circuit execution

The CircuitExecutor algorithm in QDK/Chemistry executes quantum circuits and returns measurement results. Following QDK/Chemistry’s algorithm design principles, it takes a Circuit, a shot count, and an optional QuantumErrorProfile as input and returns a CircuitExecutorData object containing bitstring counts and execution metadata.

Overview

The CircuitExecutor provides a unified interface for running quantum circuits across different backends. It is used by algorithms such as PhaseEstimation and EnergyEstimator to execute measurement circuits, but it can also be used independently for general-purpose circuit execution.

The algorithm supports:

Multiple backends

QDK/Chemistry currently ships simulator backends — QDK’s native Q# simulators (sparse-state and full-state) and Qiskit’s Aer simulator via the plugin system. The interface is designed to be extensible to additional backends.

Noise modelling

Depolarizing noise on individual gates via QuantumErrorProfile, enabling realistic simulation of noisy quantum hardware.

Flexible circuit input

Accepts circuits in OpenQASM, QIR, or native Q# representations — the Circuit class handles format conversion automatically.

Using the CircuitExecutor

Note

This algorithm is currently available only in the Python API.

This section demonstrates how to create, configure, and run a circuit executor. The run method returns a CircuitExecutorData object containing bitstring measurement counts.

Input requirements

The CircuitExecutor requires the following inputs:

Circuit

A Circuit object containing the quantum circuit to execute. The circuit can be provided in any supported format (OpenQASM, QIR, Q# callable, or Q# factory).

Shots

The number of measurement repetitions to perform.

Noise model (optional)

A QuantumErrorProfile specifying gate-level error rates. When provided, the simulator applies the specified noise during circuit execution. Not all backends support noise — see Available implementations for details.

Creating a circuit executor

from qdk_chemistry.algorithms import create

# Create the default executor (QDK sparse-state simulator)
executor = create("circuit_executor")

# Or select a specific implementation
full_state = create("circuit_executor", "qdk_full_state_simulator")
sparse_state = create("circuit_executor", "qdk_sparse_state_simulator")
aer = create("circuit_executor", "qiskit_aer_simulator")

Configuring settings

Settings vary by implementation. See Available implementations below for implementation-specific options.

# Configure the QDK full-state simulator
executor = create("circuit_executor", "qdk_full_state_simulator")
executor.settings().set("type", "cpu")
executor.settings().set("seed", 42)
# Configure the Qiskit Aer simulator
executor = create("circuit_executor", "qiskit_aer_simulator")
executor.settings().set("method", "statevector")
executor.settings().set("seed", 42)
executor.settings().set("transpile_optimization_level", 0)

Running a circuit

from qdk_chemistry.algorithms import create
from qdk_chemistry.data import Circuit

# Define a circuit in OpenQASM
circuit = Circuit(
    qasm="""
    include "stdgates.inc";
    qubit[2] q;
    bit[2] c;
    x q[0];
    cx q[0], q[1];
    c[0] = measure q[0];
    c[1] = measure q[1];
    """
)

# Execute with the QDK sparse-state simulator
executor = create("circuit_executor", "qdk_sparse_state_simulator")
result = executor.run(circuit, shots=1000)
print(f"Bitstring counts: {result.bitstring_counts}")
print(f"Total shots: {result.total_shots}")

Running with noise

from qdk_chemistry.algorithms import create
from qdk_chemistry.data import Circuit, QuantumErrorProfile

circuit = Circuit(
    qasm="""
    include "stdgates.inc";
    qubit[2] q;
    bit[2] c;
    x q[0];
    cx q[0], q[1];
    c[0] = measure q[0];
    c[1] = measure q[1];
    """
)

# Define a noise model
noise_model = QuantumErrorProfile(
    name="depolarizing",
    description="Simple depolarizing noise model",
    errors={
        "x": {"type": "depolarizing_error", "rate": 0.005, "num_qubits": 1},
        "cx": {"type": "depolarizing_error", "rate": 0.007, "num_qubits": 2},
    },
)

# Execute with noise
executor = create("circuit_executor", "qdk_full_state_simulator", type="cpu")
result = executor.run(circuit, shots=1000, noise=noise_model)
print(f"Noisy bitstring counts: {result.bitstring_counts}")

Available implementations

You can discover available implementations programmatically:

from qdk_chemistry.algorithms import registry

# List all registered circuit executor implementations
implementations = registry.available("circuit_executor")
print(
    implementations
)  # e.g. ['qdk_sparse_state_simulator', 'qdk_full_state_simulator', 'qiskit_aer_simulator']

QDK sparse-state simulator

Factory name: "qdk_sparse_state_simulator" (default)

A sparse-state simulator that efficiently represents quantum states with limited entanglement. Supports both Q# factory circuits and OpenQASM circuits.

Settings

Setting

Type

Description

seed

int

Random seed for reproducibility. Default is 42.

Note

Noise modelling is not supported by the sparse-state simulator. Use the full-state simulator for noisy simulations.

QDK full-state simulator

Factory name: "qdk_full_state_simulator"

A full-state simulator that supports CPU, GPU, and Clifford simulation modes, with optional depolarizing noise via QuantumErrorProfile.

Settings

Setting

Type

Description

type

str

Simulator type: "cpu" (default), "gpu", or "clifford".

seed

int

Random seed for reproducibility. Default is 42.

Qiskit Aer simulator

Factory name: "qiskit_aer_simulator"

Integration with Qiskit’s Aer simulator, supporting multiple simulation methods and custom noise models. This implementation is available through the Qiskit plugin.

Settings

Setting

Type

Description

method

str

Simulation method (e.g., "statevector"). Default is "statevector".

seed

int

Random seed for reproducibility. Default is 42.

transpile_optimization_level

int

Qiskit transpilation optimization level (0–3). Default is 0.

Further reading