Skip to content

Backends API Reference

HLQuantum translates its Circuit IR into the native representation of each backend. All backends implement the Backend abstract base class and return a unified ExecutionResult.

Supported Backends

Backend Framework GPU support Install extra
CudaQBackend NVIDIA CUDA-Q Yes pip install hlquantum[cudaq]
QiskitBackend IBM Qiskit Yes pip install hlquantum[qiskit]
CirqBackend Google Cirq Yes pip install hlquantum[cirq]
BraketBackend Amazon Braket No (cloud) pip install hlquantum[braket]
PennyLaneBackend Xanadu PennyLane Yes pip install hlquantum[pennylane]
IonQBackend IonQ (qiskit-ionq) No (cloud) pip install hlquantum[ionq]

Quick Examples

from hlquantum.backends import (
    CudaQBackend, QiskitBackend, CirqBackend,
    BraketBackend, PennyLaneBackend, IonQBackend,
)
import hlquantum as hlq

# CUDA-Q
result = hlq.run(circuit, backend=CudaQBackend())

# Qiskit (local Aer simulator)
result = hlq.run(circuit, backend=QiskitBackend())

# Cirq
result = hlq.run(circuit, backend=CirqBackend())

# Amazon Braket (local)
result = hlq.run(circuit, backend=BraketBackend())

# PennyLane
result = hlq.run(circuit, backend=PennyLaneBackend())

# IonQ (cloud simulator)
result = hlq.run(circuit, backend=IonQBackend(api_key="..."))

Adding a Custom Backend

from hlquantum.backends import Backend
from hlquantum.result import ExecutionResult

class MyBackend(Backend):
    @property
    def name(self) -> str:
        return "my_backend"

    def run(self, circuit, shots=1000, **kwargs):
        # Translate circuit.gates → your framework, execute, collect counts
        return ExecutionResult(counts={"00": shots}, shots=shots, backend_name=self.name)

Base Class

Abstract base class for backends.

Backend

Bases: ABC

Abstract quantum-execution backend.

Source code in hlquantum/backends/base.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Backend(ABC):
    """Abstract quantum-execution backend."""

    @property
    @abstractmethod
    def name(self) -> str: ...

    @property
    def supports_gpu(self) -> bool:
        return False

    @property
    def gpu_config(self) -> Optional[GPUConfig]:
        return getattr(self, "_gpu_config", None)

    @abstractmethod
    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs,
    ) -> ExecutionResult:
        """Execute circuit and return Result."""
        ...

    def validate(self, circuit: QuantumCircuit) -> None:
        """Validate circuit for this backend."""
        if circuit.num_qubits < 1:
            raise CircuitValidationError(f"Circuit must have at least 1 qubit.")

    def __repr__(self) -> str:
        gpu_tag = " [GPU]" if self.gpu_config and self.gpu_config.enabled else ""
        return f"<Backend: {self.name}{gpu_tag}>"

run(circuit, shots=1000, include_statevector=False, **kwargs) abstractmethod

Execute circuit and return Result.

Source code in hlquantum/backends/base.py
29
30
31
32
33
34
35
36
37
38
@abstractmethod
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs,
) -> ExecutionResult:
    """Execute circuit and return Result."""
    ...

validate(circuit)

Validate circuit for this backend.

Source code in hlquantum/backends/base.py
40
41
42
43
def validate(self, circuit: QuantumCircuit) -> None:
    """Validate circuit for this backend."""
    if circuit.num_qubits < 1:
        raise CircuitValidationError(f"Circuit must have at least 1 qubit.")

CUDA-Q

hlquantum.backends.cudaq_backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CUDA-Q backend for HLQuantum.

CudaQBackend

Bases: Backend

Execute HLQuantum circuits on NVIDIA CUDA-Q.

Source code in hlquantum/backends/cudaq_backend.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
class CudaQBackend(Backend):
    """Execute HLQuantum circuits on NVIDIA CUDA-Q."""

    def __init__(
        self,
        target: Optional[str] = None,
        gpu_config: Optional[GPUConfig] = None,
    ) -> None:
        self._gpu_config = gpu_config or GPUConfig()
        self._explicit_target = target
        self._target = self._resolve_target()

    @property
    def supports_gpu(self) -> bool:
        return True

    @property
    def name(self) -> str:
        return f"cudaq ({self._target or 'default'})"

    def _resolve_target(self) -> Optional[str]:
        if self._explicit_target is not None:
            return self._explicit_target
        if not self._gpu_config.enabled:
            return None  # let CUDA-Q use its built-in default
        if self._gpu_config.multi_gpu:
            return "nvidia-mqpu"
        if self._gpu_config.precision == GPUPrecision.FP64:
            return "nvidia-fp64"
        return "nvidia"

    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs: Any,
    ) -> ExecutionResult:
        """Sample *circuit* on CUDA-Q and return an :class:`ExecutionResult`."""
        cudaq = _require_cudaq()

        # Apply CUDA_VISIBLE_DEVICES if configured
        if self._gpu_config.enabled:
            self._gpu_config.apply_env()

        # Set the target (skip if None to use CUDA-Q built-in default)
        if self._target is not None:
            target_kwargs: Dict[str, Any] = {}
            if self._gpu_config.enabled and self._gpu_config.extra:
                target_kwargs.update(self._gpu_config.extra)
            cudaq.set_target(self._target, **target_kwargs)

        num_qubits = circuit.num_qubits
        gates = circuit.gates

        # Build a cudaq kernel dynamically
        kernel_builder = cudaq.make_kernel()
        qreg = kernel_builder.qalloc(num_qubits)

        for gate in gates:
            self._apply_gate(kernel_builder, qreg, gate, cudaq)

        logger.info(
            "Executing %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
            num_qubits,
            len(gates),
            shots,
            self._target,
            "enabled" if self._gpu_config.enabled else "disabled",
            include_statevector,
        )

        # 1. Get counts if shots > 0
        counts: Dict[str, int] = {}
        raw_result = None
        if shots > 0:
            raw_result = cudaq.sample(kernel_builder, shots_count=shots)
            for bitstring in raw_result:
                counts[bitstring] = raw_result.count(bitstring)

        # 2. Get statevector if requested
        state_vector = None
        if include_statevector:
            # Note: get_state typically works best without measurement gates
            # If the circuit has measurements, we might need a separate kernel without them
            if any(g.name == "mz" for g in gates):
                sv_builder = cudaq.make_kernel()
                sv_qreg = sv_builder.qalloc(num_qubits)
                for gate in gates:
                    if gate.name != "mz":
                        self._apply_gate(sv_builder, sv_qreg, gate, cudaq)
                state_vector = cudaq.get_state(sv_builder)
            else:
                state_vector = cudaq.get_state(kernel_builder)

        return ExecutionResult(
            counts=counts,
            shots=shots,
            backend_name=self.name,
            raw=raw_result,
            state_vector=state_vector,
            metadata={"gpu_config": repr(self._gpu_config)},
        )

    @staticmethod
    def _apply_gate(builder: Any, qreg: Any, gate: Gate, cudaq: Any) -> None:
        name = gate.name
        if name == "mz":
            builder.mz(qreg[gate.targets[0]])
        elif name in ("h", "x", "y", "z", "s", "t"):
            getattr(builder, name)(qreg[gate.targets[0]])
        elif name in ("rx", "ry", "rz"):
            getattr(builder, name)(gate.params[0], qreg[gate.targets[0]])
        elif name == "cx":
            builder.cx(qreg[gate.controls[0]], qreg[gate.targets[0]])
        elif name == "cz":
            builder.cz(qreg[gate.controls[0]], qreg[gate.targets[0]])
        elif name == "swap":
            builder.swap(qreg[gate.targets[0]], qreg[gate.targets[1]])
        elif name == "ccx":
            builder.ccx(qreg[gate.controls[0]], qreg[gate.controls[1]], qreg[gate.targets[0]])
        else:
            raise ValueError(f"CudaQBackend does not support gate: {name!r}")

run(circuit, shots=1000, include_statevector=False, **kwargs)

Sample circuit on CUDA-Q and return an :class:ExecutionResult.

Source code in hlquantum/backends/cudaq_backend.py
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs: Any,
) -> ExecutionResult:
    """Sample *circuit* on CUDA-Q and return an :class:`ExecutionResult`."""
    cudaq = _require_cudaq()

    # Apply CUDA_VISIBLE_DEVICES if configured
    if self._gpu_config.enabled:
        self._gpu_config.apply_env()

    # Set the target (skip if None to use CUDA-Q built-in default)
    if self._target is not None:
        target_kwargs: Dict[str, Any] = {}
        if self._gpu_config.enabled and self._gpu_config.extra:
            target_kwargs.update(self._gpu_config.extra)
        cudaq.set_target(self._target, **target_kwargs)

    num_qubits = circuit.num_qubits
    gates = circuit.gates

    # Build a cudaq kernel dynamically
    kernel_builder = cudaq.make_kernel()
    qreg = kernel_builder.qalloc(num_qubits)

    for gate in gates:
        self._apply_gate(kernel_builder, qreg, gate, cudaq)

    logger.info(
        "Executing %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
        num_qubits,
        len(gates),
        shots,
        self._target,
        "enabled" if self._gpu_config.enabled else "disabled",
        include_statevector,
    )

    # 1. Get counts if shots > 0
    counts: Dict[str, int] = {}
    raw_result = None
    if shots > 0:
        raw_result = cudaq.sample(kernel_builder, shots_count=shots)
        for bitstring in raw_result:
            counts[bitstring] = raw_result.count(bitstring)

    # 2. Get statevector if requested
    state_vector = None
    if include_statevector:
        # Note: get_state typically works best without measurement gates
        # If the circuit has measurements, we might need a separate kernel without them
        if any(g.name == "mz" for g in gates):
            sv_builder = cudaq.make_kernel()
            sv_qreg = sv_builder.qalloc(num_qubits)
            for gate in gates:
                if gate.name != "mz":
                    self._apply_gate(sv_builder, sv_qreg, gate, cudaq)
            state_vector = cudaq.get_state(sv_builder)
        else:
            state_vector = cudaq.get_state(kernel_builder)

    return ExecutionResult(
        counts=counts,
        shots=shots,
        backend_name=self.name,
        raw=raw_result,
        state_vector=state_vector,
        metadata={"gpu_config": repr(self._gpu_config)},
    )

Qiskit

hlquantum.backends.qiskit_backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

IBM Qiskit backend for HLQuantum.

QiskitBackend

Bases: Backend

Execute HLQuantum circuits using IBM Qiskit.

Source code in hlquantum/backends/qiskit_backend.py
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
class QiskitBackend(Backend):
    """Execute HLQuantum circuits using IBM Qiskit."""

    def __init__(
        self,
        backend: Optional[Any] = None,
        transpile: bool = True,
        optimization_level: int = 1,
        gpu_config: Optional[GPUConfig] = None,
    ) -> None:
        self._user_backend = backend
        self._transpile = transpile
        self._optimization_level = optimization_level
        self._gpu_config = gpu_config or GPUConfig()

    @property
    def supports_gpu(self) -> bool:
        return True

    @property
    def name(self) -> str:
        if self._user_backend is not None:
            label = getattr(self._user_backend, "name", str(self._user_backend))
            return f"qiskit ({label})"
        suffix = " GPU" if self._gpu_config.enabled else ""
        return f"qiskit (aer_simulator{suffix})"

    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs: Any,
    ) -> ExecutionResult:
        """Transpile & run *circuit* on the configured Qiskit backend."""
        qiskit = _require_qiskit()

        qk_circuit = self._translate(circuit, qiskit)

        # Resolve backend
        if self._user_backend is not None:
            qk_backend = self._user_backend
        else:
            qk_backend = self._build_aer_backend()

        # If statevector requested, add the save instruction (Aer specific)
        if include_statevector:
            # Check if backend supports save_statevector (effectively Aer)
            if hasattr(qk_circuit, "save_statevector"):
                qk_circuit.save_statevector()
            else:
                try:
                    import qiskit_aer.library as aer_lib
                    qk_circuit.append(aer_lib.SaveStatevector(circuit.num_qubits), qk_circuit.qubits)
                except (ImportError, Exception):
                    logger.warning("Backend may not support state vector retrieval.")

        # Apply CUDA_VISIBLE_DEVICES if configured
        if self._gpu_config.enabled:
            self._gpu_config.apply_env()

        # Optionally transpile
        if self._transpile:
            from qiskit import transpile as qk_transpile  # type: ignore[import-untyped]
            qk_circuit = qk_transpile(
                qk_circuit,
                backend=qk_backend,
                optimization_level=self._optimization_level,
            )

        logger.info(
            "Running %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
            circuit.num_qubits,
            len(circuit),
            shots,
            self.name,
            "enabled" if self._gpu_config.enabled else "disabled",
            include_statevector,
        )

        # Qiskit requires shots > 0 for run() usually, unless we skip sampling
        actual_shots = shots if shots > 0 else 1
        job = qk_backend.run(qk_circuit, shots=actual_shots, **kwargs)
        raw_result = job.result()

        # Counts
        counts: Dict[str, int] = {}
        if shots > 0:
            try:
                raw_counts = raw_result.get_counts()
                if isinstance(raw_counts, list): # handle multiple circuits if ever needed
                    raw_counts = raw_counts[0]
                for bitstring, count in raw_counts.items():
                    counts[bitstring.replace(" ", "")] = count
            except Exception:
                pass

        # Statevector
        state_vector = None
        if include_statevector:
            try:
                state_vector = raw_result.get_statevector()
            except Exception:
                logger.warning("Failed to retrieve state vector from result.")

        return ExecutionResult(
            counts=counts,
            shots=shots,
            backend_name=self.name,
            raw=raw_result,
            state_vector=state_vector,
            metadata={"gpu_config": repr(self._gpu_config)},
        )

    def _build_aer_backend(self) -> Any:
        AerSimulator = _require_aer()
        aer_kwargs: Dict[str, Any] = {}
        if self._gpu_config.enabled:
            aer_kwargs["device"] = "GPU"
            if self._gpu_config.custatevec:
                aer_kwargs["cuStateVec_enable"] = True
            logger.info("Configuring AerSimulator with GPU (cuStateVec=%s)", self._gpu_config.custatevec)
        return AerSimulator(**aer_kwargs)

    @staticmethod
    def _translate(circuit: QuantumCircuit, qiskit: Any) -> Any:
        n = circuit.num_qubits
        qk = qiskit.QuantumCircuit(n, n)
        measure_targets = []

        for gate in circuit.gates:
            name = gate.name
            if name == "h":       qk.h(gate.targets[0])
            elif name == "x":     qk.x(gate.targets[0])
            elif name == "y":     qk.y(gate.targets[0])
            elif name == "z":     qk.z(gate.targets[0])
            elif name == "s":     qk.s(gate.targets[0])
            elif name == "t":     qk.t(gate.targets[0])
            elif name == "rx":    qk.rx(gate.params[0], gate.targets[0])
            elif name == "ry":    qk.ry(gate.params[0], gate.targets[0])
            elif name == "rz":    qk.rz(gate.params[0], gate.targets[0])
            elif name == "cx":    qk.cx(gate.controls[0], gate.targets[0])
            elif name == "cz":    qk.cz(gate.controls[0], gate.targets[0])
            elif name == "swap":  qk.swap(gate.targets[0], gate.targets[1])
            elif name == "ccx":   qk.ccx(gate.controls[0], gate.controls[1], gate.targets[0])
            elif name == "mz":    measure_targets.append(gate.targets[0])
            else: raise ValueError(f"QiskitBackend does not support gate: {name!r}")

        for t in measure_targets:
            qk.measure(t, t)
        return qk

run(circuit, shots=1000, include_statevector=False, **kwargs)

Transpile & run circuit on the configured Qiskit backend.

Source code in hlquantum/backends/qiskit_backend.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs: Any,
) -> ExecutionResult:
    """Transpile & run *circuit* on the configured Qiskit backend."""
    qiskit = _require_qiskit()

    qk_circuit = self._translate(circuit, qiskit)

    # Resolve backend
    if self._user_backend is not None:
        qk_backend = self._user_backend
    else:
        qk_backend = self._build_aer_backend()

    # If statevector requested, add the save instruction (Aer specific)
    if include_statevector:
        # Check if backend supports save_statevector (effectively Aer)
        if hasattr(qk_circuit, "save_statevector"):
            qk_circuit.save_statevector()
        else:
            try:
                import qiskit_aer.library as aer_lib
                qk_circuit.append(aer_lib.SaveStatevector(circuit.num_qubits), qk_circuit.qubits)
            except (ImportError, Exception):
                logger.warning("Backend may not support state vector retrieval.")

    # Apply CUDA_VISIBLE_DEVICES if configured
    if self._gpu_config.enabled:
        self._gpu_config.apply_env()

    # Optionally transpile
    if self._transpile:
        from qiskit import transpile as qk_transpile  # type: ignore[import-untyped]
        qk_circuit = qk_transpile(
            qk_circuit,
            backend=qk_backend,
            optimization_level=self._optimization_level,
        )

    logger.info(
        "Running %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
        circuit.num_qubits,
        len(circuit),
        shots,
        self.name,
        "enabled" if self._gpu_config.enabled else "disabled",
        include_statevector,
    )

    # Qiskit requires shots > 0 for run() usually, unless we skip sampling
    actual_shots = shots if shots > 0 else 1
    job = qk_backend.run(qk_circuit, shots=actual_shots, **kwargs)
    raw_result = job.result()

    # Counts
    counts: Dict[str, int] = {}
    if shots > 0:
        try:
            raw_counts = raw_result.get_counts()
            if isinstance(raw_counts, list): # handle multiple circuits if ever needed
                raw_counts = raw_counts[0]
            for bitstring, count in raw_counts.items():
                counts[bitstring.replace(" ", "")] = count
        except Exception:
            pass

    # Statevector
    state_vector = None
    if include_statevector:
        try:
            state_vector = raw_result.get_statevector()
        except Exception:
            logger.warning("Failed to retrieve state vector from result.")

    return ExecutionResult(
        counts=counts,
        shots=shots,
        backend_name=self.name,
        raw=raw_result,
        state_vector=state_vector,
        metadata={"gpu_config": repr(self._gpu_config)},
    )

Cirq

hlquantum.backends.cirq_backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Google Cirq backend for HLQuantum.

CirqBackend

Bases: Backend

Execute HLQuantum circuits using Google Cirq.

Source code in hlquantum/backends/cirq_backend.py
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class CirqBackend(Backend):
    """Execute HLQuantum circuits using Google Cirq."""

    def __init__(
        self,
        simulator: Optional[Any] = None,
        noise_model: Optional[Any] = None,
        gpu_config: Optional[GPUConfig] = None,
    ) -> None:
        self._user_simulator = simulator
        self._noise_model = noise_model
        self._gpu_config = gpu_config or GPUConfig()

    @property
    def supports_gpu(self) -> bool:
        return True

    @property
    def name(self) -> str:
        if self._gpu_config.enabled:
            return "cirq (qsim GPU)"
        if self._user_simulator is not None:
            return f"cirq ({type(self._user_simulator).__name__})"
        return "cirq (Simulator)"

    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs: Any,
    ) -> ExecutionResult:
        """Simulate *circuit* with Cirq and return an :class:`ExecutionResult`."""
        cirq = _require_cirq()

        cirq_circuit, qubits, measured_keys = self._translate(circuit, cirq)

        # Resolve simulator
        sim = self._build_simulator(cirq)

        # Apply CUDA_VISIBLE_DEVICES if configured
        if self._gpu_config.enabled:
            self._gpu_config.apply_env()

        logger.info(
            "Simulating %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
            circuit.num_qubits,
            len(circuit),
            shots,
            self.name,
            "enabled" if self._gpu_config.enabled else "disabled",
            include_statevector,
        )

        # 1. Get counts if shots > 0
        counts: Dict[str, int] = {}
        raw_result = None
        if shots > 0:
            raw_result = sim.run(cirq_circuit, repetitions=shots, **kwargs)
            if measured_keys:
                import numpy as np
                arrays = [raw_result.measurements[k] for k in measured_keys]
                combined = np.concatenate(arrays, axis=1)
                for row in combined:
                    bitstring = "".join(str(int(b)) for b in row)
                    counts[bitstring] = counts.get(bitstring, 0) + 1
            else:
                counts[""] = shots

        # 2. Get statevector if requested
        state_vector = None
        if include_statevector:
            # For a "pure" state vector, we often want it without measurements
            if measured_keys:
                # Create a version without measurements
                sv_ops = [op for op in cirq_circuit.all_operations() if not cirq.is_measurement(op)]
                sv_circuit = cirq.Circuit(sv_ops)
                sv_res = sim.simulate(sv_circuit)
            else:
                sv_res = sim.simulate(cirq_circuit)

            # Extract state vector (handling density matrix simulator if needed)
            if hasattr(sv_res, "final_state_vector"):
                state_vector = sv_res.final_state_vector
            elif hasattr(sv_res, "final_density_matrix"):
                state_vector = sv_res.final_density_matrix

        return ExecutionResult(
            counts=counts,
            shots=shots,
            backend_name=self.name,
            raw=raw_result,
            state_vector=state_vector,
            metadata={"gpu_config": repr(self._gpu_config)},
        )

    def _build_simulator(self, cirq: Any) -> Any:
        if self._gpu_config.enabled:
            qsimcirq = _require_qsimcirq()
            qsim_options = {"use_gpu": True, **self._gpu_config.extra}
            logger.info("Configuring qsim GPU simulator: %s", qsim_options)
            return qsimcirq.QSimSimulator(qsim_options)
        if self._user_simulator is not None:
            return self._user_simulator
        if self._noise_model is not None:
            return cirq.DensityMatrixSimulator(noise=self._noise_model)
        return cirq.Simulator()

    @staticmethod
    def _translate(circuit: QuantumCircuit, cirq: Any):
        qubits = cirq.LineQubit.range(circuit.num_qubits)
        ops = []
        measured_keys: List[str] = []

        for gate in circuit.gates:
            name = gate.name
            if name == "h":      ops.append(cirq.H(qubits[gate.targets[0]]))
            elif name == "x":    ops.append(cirq.X(qubits[gate.targets[0]]))
            elif name == "y":    ops.append(cirq.Y(qubits[gate.targets[0]]))
            elif name == "z":    ops.append(cirq.Z(qubits[gate.targets[0]]))
            elif name == "s":    ops.append(cirq.S(qubits[gate.targets[0]]))
            elif name == "t":    ops.append(cirq.T(qubits[gate.targets[0]]))
            elif name == "rx":   ops.append(cirq.rx(gate.params[0]).on(qubits[gate.targets[0]]))
            elif name == "ry":   ops.append(cirq.ry(gate.params[0]).on(qubits[gate.targets[0]]))
            elif name == "rz":   ops.append(cirq.rz(gate.params[0]).on(qubits[gate.targets[0]]))
            elif name == "cx":   ops.append(cirq.CNOT(qubits[gate.controls[0]], qubits[gate.targets[0]]))
            elif name == "cz":   ops.append(cirq.CZ(qubits[gate.controls[0]], qubits[gate.targets[0]]))
            elif name == "swap": ops.append(cirq.SWAP(qubits[gate.targets[0]], qubits[gate.targets[1]]))
            elif name == "ccx":  ops.append(cirq.CCX(qubits[gate.controls[0]], qubits[gate.controls[1]], qubits[gate.targets[0]]))
            elif name == "mz":
                key = f"m{gate.targets[0]}"
                measured_keys.append(key)
                ops.append(cirq.measure(qubits[gate.targets[0]], key=key))
            else:
                raise ValueError(f"CirqBackend does not support gate: {name!r}")

        return cirq.Circuit(ops), qubits, measured_keys

run(circuit, shots=1000, include_statevector=False, **kwargs)

Simulate circuit with Cirq and return an :class:ExecutionResult.

Source code in hlquantum/backends/cirq_backend.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs: Any,
) -> ExecutionResult:
    """Simulate *circuit* with Cirq and return an :class:`ExecutionResult`."""
    cirq = _require_cirq()

    cirq_circuit, qubits, measured_keys = self._translate(circuit, cirq)

    # Resolve simulator
    sim = self._build_simulator(cirq)

    # Apply CUDA_VISIBLE_DEVICES if configured
    if self._gpu_config.enabled:
        self._gpu_config.apply_env()

    logger.info(
        "Simulating %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
        circuit.num_qubits,
        len(circuit),
        shots,
        self.name,
        "enabled" if self._gpu_config.enabled else "disabled",
        include_statevector,
    )

    # 1. Get counts if shots > 0
    counts: Dict[str, int] = {}
    raw_result = None
    if shots > 0:
        raw_result = sim.run(cirq_circuit, repetitions=shots, **kwargs)
        if measured_keys:
            import numpy as np
            arrays = [raw_result.measurements[k] for k in measured_keys]
            combined = np.concatenate(arrays, axis=1)
            for row in combined:
                bitstring = "".join(str(int(b)) for b in row)
                counts[bitstring] = counts.get(bitstring, 0) + 1
        else:
            counts[""] = shots

    # 2. Get statevector if requested
    state_vector = None
    if include_statevector:
        # For a "pure" state vector, we often want it without measurements
        if measured_keys:
            # Create a version without measurements
            sv_ops = [op for op in cirq_circuit.all_operations() if not cirq.is_measurement(op)]
            sv_circuit = cirq.Circuit(sv_ops)
            sv_res = sim.simulate(sv_circuit)
        else:
            sv_res = sim.simulate(cirq_circuit)

        # Extract state vector (handling density matrix simulator if needed)
        if hasattr(sv_res, "final_state_vector"):
            state_vector = sv_res.final_state_vector
        elif hasattr(sv_res, "final_density_matrix"):
            state_vector = sv_res.final_density_matrix

    return ExecutionResult(
        counts=counts,
        shots=shots,
        backend_name=self.name,
        raw=raw_result,
        state_vector=state_vector,
        metadata={"gpu_config": repr(self._gpu_config)},
    )

Amazon Braket

hlquantum.backends.braket_backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Amazon Braket backend for HLQuantum.

BraketBackend

Bases: Backend

Execute HLQuantum circuits using Amazon Braket.

Source code in hlquantum/backends/braket_backend.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
class BraketBackend(Backend):
    """Execute HLQuantum circuits using Amazon Braket."""

    def __init__(self, device: Optional[Any] = None, s3_destination: Optional[tuple] = None) -> None:
        self._user_device = device
        self._s3_destination = s3_destination

    @property
    def name(self) -> str:
        if self._user_device is not None:
            label = getattr(self._user_device, "name", str(self._user_device))
            return f"braket ({label})"
        return "braket (LocalSimulator)"

    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs: Any,
    ) -> ExecutionResult:
        """Execute *circuit* with Amazon Braket and return an :class:`ExecutionResult`."""
        BraketCircuit, LocalSimulator = _require_braket()
        braket_circuit = self._translate(circuit, BraketCircuit)

        device = self._user_device if self._user_device is not None else LocalSimulator()

        logger.info(
            "Running %d-qubit circuit (%d gates) for %d shots on %s (SV: %s)",
            circuit.num_qubits,
            len(circuit),
            shots,
            self.name,
            include_statevector,
        )

        counts: Dict[str, int] = {}
        raw_result = None
        state_vector = None

        # 1. Get counts if shots > 0
        if shots > 0:
            run_kwargs: Dict[str, Any] = {"shots": shots, **kwargs}
            if self._s3_destination is not None:
                run_kwargs["s3_destination_folder"] = self._s3_destination

            task = device.run(braket_circuit, **run_kwargs)
            raw_result = task.result()

            for bitstring, count in raw_result.measurement_counts.items():
                counts[bitstring] = count

        # 2. Get statevector if requested
        if include_statevector:
            try:
                from braket.circuits import result_types as braket_rt
                sv_circuit = self._translate(circuit, BraketCircuit)
                sv_circuit.state_vector()
                sv_kwargs = {**kwargs, "shots": 0}
                sv_task = device.run(sv_circuit, **sv_kwargs)
                sv_result = sv_task.result()
                # Access state vector from result_types
                if sv_result.result_types:
                    state_vector = sv_result.result_types[0]["value"]
            except Exception as exc:
                logger.warning("Could not retrieve state vector: %s", exc)
            if raw_result is None:
                raw_result = sv_result

        return ExecutionResult(
            counts=counts,
            shots=shots,
            backend_name=self.name,
            raw=raw_result,
            state_vector=state_vector,
        )

    @staticmethod
    def _translate(circuit: QuantumCircuit, BraketCircuit: type) -> Any:
        bc = BraketCircuit()
        for gate in circuit.gates:
            name = gate.name
            t0 = gate.targets[0]
            if name == "h":      bc.h(t0)
            elif name == "x":    bc.x(t0)
            elif name == "y":    bc.y(t0)
            elif name == "z":    bc.z(t0)
            elif name == "s":    bc.s(t0)
            elif name == "t":    bc.t(t0)
            elif name == "rx":   bc.rx(t0, gate.params[0])
            elif name == "ry":   bc.ry(t0, gate.params[0])
            elif name == "rz":   bc.rz(t0, gate.params[0])
            elif name == "cx":   bc.cnot(gate.controls[0], t0)
            elif name == "cz":   bc.cz(gate.controls[0], t0)
            elif name == "swap": bc.swap(gate.targets[0], gate.targets[1])
            elif name == "ccx":  bc.ccnot(gate.controls[0], gate.controls[1], t0)
            elif name == "mz":   pass
            else: raise ValueError(f"BraketBackend does not support gate: {name!r}")
        return bc

run(circuit, shots=1000, include_statevector=False, **kwargs)

Execute circuit with Amazon Braket and return an :class:ExecutionResult.

Source code in hlquantum/backends/braket_backend.py
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs: Any,
) -> ExecutionResult:
    """Execute *circuit* with Amazon Braket and return an :class:`ExecutionResult`."""
    BraketCircuit, LocalSimulator = _require_braket()
    braket_circuit = self._translate(circuit, BraketCircuit)

    device = self._user_device if self._user_device is not None else LocalSimulator()

    logger.info(
        "Running %d-qubit circuit (%d gates) for %d shots on %s (SV: %s)",
        circuit.num_qubits,
        len(circuit),
        shots,
        self.name,
        include_statevector,
    )

    counts: Dict[str, int] = {}
    raw_result = None
    state_vector = None

    # 1. Get counts if shots > 0
    if shots > 0:
        run_kwargs: Dict[str, Any] = {"shots": shots, **kwargs}
        if self._s3_destination is not None:
            run_kwargs["s3_destination_folder"] = self._s3_destination

        task = device.run(braket_circuit, **run_kwargs)
        raw_result = task.result()

        for bitstring, count in raw_result.measurement_counts.items():
            counts[bitstring] = count

    # 2. Get statevector if requested
    if include_statevector:
        try:
            from braket.circuits import result_types as braket_rt
            sv_circuit = self._translate(circuit, BraketCircuit)
            sv_circuit.state_vector()
            sv_kwargs = {**kwargs, "shots": 0}
            sv_task = device.run(sv_circuit, **sv_kwargs)
            sv_result = sv_task.result()
            # Access state vector from result_types
            if sv_result.result_types:
                state_vector = sv_result.result_types[0]["value"]
        except Exception as exc:
            logger.warning("Could not retrieve state vector: %s", exc)
        if raw_result is None:
            raw_result = sv_result

    return ExecutionResult(
        counts=counts,
        shots=shots,
        backend_name=self.name,
        raw=raw_result,
        state_vector=state_vector,
    )

PennyLane

hlquantum.backends.pennylane_backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PennyLane (Xanadu) backend for HLQuantum.

PennyLaneBackend

Bases: Backend

Execute HLQuantum circuits using Xanadu PennyLane.

Source code in hlquantum/backends/pennylane_backend.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
class PennyLaneBackend(Backend):
    """Execute HLQuantum circuits using Xanadu PennyLane."""

    def __init__(
        self,
        device_name: Optional[str] = None,
        device_kwargs: Optional[Dict[str, Any]] = None,
        gpu_config: Optional[GPUConfig] = None,
    ) -> None:
        self._gpu_config = gpu_config or GPUConfig()
        self._explicit_device = device_name
        self._device_name = self._resolve_device()
        self._device_kwargs = device_kwargs or {}

    @property
    def supports_gpu(self) -> bool:
        return True

    @property
    def name(self) -> str:
        return f"pennylane ({self._device_name})"

    def _resolve_device(self) -> str:
        if self._explicit_device is not None:
            return self._explicit_device
        if self._gpu_config.enabled:
            return "lightning.gpu"
        return "default.qubit"

    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs: Any,
    ) -> ExecutionResult:
        """Execute *circuit* with PennyLane and return an :class:`ExecutionResult`."""
        qml = _require_pennylane()
        import numpy as np

        num_qubits = circuit.num_qubits
        gates = circuit.gates

        # Apply CUDA_VISIBLE_DEVICES if configured
        if self._gpu_config.enabled:
            self._gpu_config.apply_env()

        # Shared device kwargs
        dev_kwargs = {**self._device_kwargs, **self._gpu_config.extra}

        logger.info(
            "Executing %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
            num_qubits,
            len(gates),
            shots,
            self.name,
            "enabled" if self._gpu_config.enabled else "disabled",
            include_statevector,
        )

        # 1. Get counts if shots > 0
        counts: Dict[str, int] = {}
        raw_result = None
        if shots > 0:
            measured: List[int] = [g.targets[0] for g in gates if g.name == "mz"]
            if not measured:
                measured = list(range(num_qubits))

            dev = qml.device(self._device_name, wires=num_qubits, **dev_kwargs)

            @qml.qnode(dev, shots=shots)
            def qnode_counts():
                for gate in gates:
                    self._apply_gate(gate, qml)
                return qml.counts(wires=measured)

            raw_result = qnode_counts()

            for key, count in raw_result.items():
                if isinstance(key, (int, np.integer)):
                    bitstring = str(int(key))
                elif isinstance(key, (tuple, list, np.ndarray)):
                    bitstring = "".join(str(int(b)) for b in key)
                else:
                    bitstring = str(key)
                counts[bitstring] = int(count)

        # 2. Get statevector if requested
        state_vector = None
        if include_statevector:
            sv_dev = qml.device(self._device_name, wires=num_qubits, **dev_kwargs)

            @qml.qnode(sv_dev)
            def qnode_state():
                # Apply gates EXCEPT measurements (PL statevector is usually pre-measurement)
                for gate in gates:
                    if gate.name != "mz":
                        self._apply_gate(gate, qml)
                return qml.state()

            state_vector = qnode_state()

        return ExecutionResult(
            counts=counts,
            shots=shots,
            backend_name=self.name,
            raw=raw_result,
            state_vector=state_vector,
            metadata={"gpu_config": repr(self._gpu_config)},
        )

    @staticmethod
    def _apply_gate(gate: Gate, qml: Any) -> None:
        name = gate.name
        t0 = gate.targets[0]
        if name == "h":      qml.Hadamard(wires=t0)
        elif name == "x":    qml.PauliX(wires=t0)
        elif name == "y":    qml.PauliY(wires=t0)
        elif name == "z":    qml.PauliZ(wires=t0)
        elif name == "s":    qml.S(wires=t0)
        elif name == "t":    qml.T(wires=t0)
        elif name == "rx":   qml.RX(gate.params[0], wires=t0)
        elif name == "ry":   qml.RY(gate.params[0], wires=t0)
        elif name == "rz":   qml.RZ(gate.params[0], wires=t0)
        elif name == "cx":   qml.CNOT(wires=[gate.controls[0], t0])
        elif name == "cz":   qml.CZ(wires=[gate.controls[0], t0])
        elif name == "swap": qml.SWAP(wires=[gate.targets[0], gate.targets[1]])
        elif name == "ccx":  qml.Toffoli(wires=[gate.controls[0], gate.controls[1], t0])
        elif name == "mz":   pass
        else: raise ValueError(f"PennyLaneBackend does not support gate: {name!r}")

run(circuit, shots=1000, include_statevector=False, **kwargs)

Execute circuit with PennyLane and return an :class:ExecutionResult.

Source code in hlquantum/backends/pennylane_backend.py
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs: Any,
) -> ExecutionResult:
    """Execute *circuit* with PennyLane and return an :class:`ExecutionResult`."""
    qml = _require_pennylane()
    import numpy as np

    num_qubits = circuit.num_qubits
    gates = circuit.gates

    # Apply CUDA_VISIBLE_DEVICES if configured
    if self._gpu_config.enabled:
        self._gpu_config.apply_env()

    # Shared device kwargs
    dev_kwargs = {**self._device_kwargs, **self._gpu_config.extra}

    logger.info(
        "Executing %d-qubit circuit (%d gates) for %d shots on %s (GPU: %s, SV: %s)",
        num_qubits,
        len(gates),
        shots,
        self.name,
        "enabled" if self._gpu_config.enabled else "disabled",
        include_statevector,
    )

    # 1. Get counts if shots > 0
    counts: Dict[str, int] = {}
    raw_result = None
    if shots > 0:
        measured: List[int] = [g.targets[0] for g in gates if g.name == "mz"]
        if not measured:
            measured = list(range(num_qubits))

        dev = qml.device(self._device_name, wires=num_qubits, **dev_kwargs)

        @qml.qnode(dev, shots=shots)
        def qnode_counts():
            for gate in gates:
                self._apply_gate(gate, qml)
            return qml.counts(wires=measured)

        raw_result = qnode_counts()

        for key, count in raw_result.items():
            if isinstance(key, (int, np.integer)):
                bitstring = str(int(key))
            elif isinstance(key, (tuple, list, np.ndarray)):
                bitstring = "".join(str(int(b)) for b in key)
            else:
                bitstring = str(key)
            counts[bitstring] = int(count)

    # 2. Get statevector if requested
    state_vector = None
    if include_statevector:
        sv_dev = qml.device(self._device_name, wires=num_qubits, **dev_kwargs)

        @qml.qnode(sv_dev)
        def qnode_state():
            # Apply gates EXCEPT measurements (PL statevector is usually pre-measurement)
            for gate in gates:
                if gate.name != "mz":
                    self._apply_gate(gate, qml)
            return qml.state()

        state_vector = qnode_state()

    return ExecutionResult(
        counts=counts,
        shots=shots,
        backend_name=self.name,
        raw=raw_result,
        state_vector=state_vector,
        metadata={"gpu_config": repr(self._gpu_config)},
    )

IonQ

hlquantum.backends.ionq_backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

IonQ backend for HLQuantum.

Supports execution on IonQ simulators and trapped-ion QPUs via the qiskit-ionq provider. When no explicit backend object is supplied the provider connects to the IonQ cloud simulator.

Prerequisites ~~~~~~~~~~~~~ * pip install qiskit qiskit-ionq * An IonQ API key, supplied either as the api_key constructor argument or through the IONQ_API_KEY / QISKIT_IONQ_API_TOKEN environment variable.

See https://docs.ionq.com and https://github.com/qiskit-partners/qiskit-ionq for full documentation.

IonQBackend

Bases: Backend

Execute HLQuantum circuits on IonQ hardware or simulators.

Parameters

backend_name : str, optional Name of the IonQ backend to use (default "ionq_simulator"). Common choices:

* ``"ionq_simulator"`` – cloud-based ideal simulator
* ``"ionq_qpu"``       – IonQ Aria / Forte trapped-ion QPU

api_key : str, optional IonQ API key. Falls back to the IONQ_API_KEY or QISKIT_IONQ_API_TOKEN environment variables when omitted. backend : object, optional A pre-configured Qiskit Backend object. When provided, backend_name and api_key are ignored. transpile : bool, optional Whether to transpile the circuit before execution (default True). optimization_level : int, optional Qiskit transpiler optimisation level (0–3, default 1).

Source code in hlquantum/backends/ionq_backend.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class IonQBackend(Backend):
    """Execute HLQuantum circuits on IonQ hardware or simulators.

    Parameters
    ----------
    backend_name : str, optional
        Name of the IonQ backend to use (default ``"ionq_simulator"``).
        Common choices:

        * ``"ionq_simulator"`` – cloud-based ideal simulator
        * ``"ionq_qpu"``       – IonQ Aria / Forte trapped-ion QPU
    api_key : str, optional
        IonQ API key.  Falls back to the ``IONQ_API_KEY`` or
        ``QISKIT_IONQ_API_TOKEN`` environment variables when omitted.
    backend : object, optional
        A pre-configured Qiskit ``Backend`` object.  When provided,
        *backend_name* and *api_key* are ignored.
    transpile : bool, optional
        Whether to transpile the circuit before execution (default *True*).
    optimization_level : int, optional
        Qiskit transpiler optimisation level (0–3, default 1).
    """

    def __init__(
        self,
        backend_name: str = "ionq_simulator",
        api_key: Optional[str] = None,
        backend: Optional[Any] = None,
        transpile: bool = True,
        optimization_level: int = 1,
    ) -> None:
        self._backend_name = backend_name
        self._api_key = api_key
        self._user_backend = backend
        self._transpile = transpile
        self._optimization_level = optimization_level

    # ----- Backend interface ------------------------------------------------

    @property
    def name(self) -> str:
        if self._user_backend is not None:
            label = getattr(self._user_backend, "name", str(self._user_backend))
            return f"ionq ({label})"
        return f"ionq ({self._backend_name})"

    def run(
        self,
        circuit: QuantumCircuit,
        shots: int = 1000,
        include_statevector: bool = False,
        **kwargs: Any,
    ) -> ExecutionResult:
        """Translate, transpile and execute *circuit* on the IonQ backend.

        Parameters
        ----------
        circuit : QuantumCircuit
            The HLQuantum circuit to execute.
        shots : int, optional
            Number of measurement shots (default 1000).
        include_statevector : bool, optional
            If *True*, attempt to retrieve the state vector.  Note that
            state-vector retrieval is only supported on simulators.
        **kwargs
            Forwarded to the underlying Qiskit ``backend.run()`` call.
        """
        qiskit = _require_qiskit()
        qk_circuit = self._translate(circuit, qiskit)

        # Resolve backend
        qk_backend = self._resolve_backend()

        # Optional: statevector save instruction (simulator only)
        if include_statevector:
            try:
                qk_circuit.save_statevector()
            except Exception:
                logger.warning(
                    "State-vector save is not supported on this IonQ backend; "
                    "the result will not contain a state vector."
                )

        # Transpile
        if self._transpile:
            from qiskit import transpile as qk_transpile  # type: ignore[import-untyped]
            qk_circuit = qk_transpile(
                qk_circuit,
                backend=qk_backend,
                optimization_level=self._optimization_level,
            )

        logger.info(
            "Running %d-qubit circuit (%d gates) for %d shots on %s (SV: %s)",
            circuit.num_qubits,
            len(circuit),
            shots,
            self.name,
            include_statevector,
        )

        actual_shots = max(shots, 1)
        job = qk_backend.run(qk_circuit, shots=actual_shots, **kwargs)
        raw_result = job.result()

        # --- Counts --------------------------------------------------------
        counts: Dict[str, int] = {}
        if shots > 0:
            try:
                raw_counts = raw_result.get_counts()
                if isinstance(raw_counts, list):
                    raw_counts = raw_counts[0]
                for bitstring, count in raw_counts.items():
                    counts[bitstring.replace(" ", "")] = count
            except Exception:
                logger.warning("Could not extract measurement counts from the IonQ result.")

        # --- Statevector ---------------------------------------------------
        state_vector = None
        if include_statevector:
            try:
                state_vector = raw_result.get_statevector()
            except Exception:
                logger.warning("Failed to retrieve state vector from result.")

        return ExecutionResult(
            counts=counts,
            shots=shots,
            backend_name=self.name,
            raw=raw_result,
            state_vector=state_vector,
            metadata={"ionq_backend": self._backend_name},
        )

    # ----- Helpers ----------------------------------------------------------

    def _resolve_backend(self) -> Any:
        """Return the Qiskit ``Backend`` object to use for execution."""
        if self._user_backend is not None:
            return self._user_backend
        provider = _require_ionq_provider(self._api_key)
        return provider.get_backend(self._backend_name)

    @staticmethod
    def _translate(circuit: QuantumCircuit, qiskit: Any) -> Any:
        """Convert an HLQuantum ``QuantumCircuit`` into a Qiskit ``QuantumCircuit``."""
        n = circuit.num_qubits
        qk = qiskit.QuantumCircuit(n, n)
        measure_targets: List[int] = []

        for gate in circuit.gates:
            name = gate.name
            t0 = gate.targets[0]
            if name == "h":       qk.h(t0)
            elif name == "x":     qk.x(t0)
            elif name == "y":     qk.y(t0)
            elif name == "z":     qk.z(t0)
            elif name == "s":     qk.s(t0)
            elif name == "t":     qk.t(t0)
            elif name == "rx":    qk.rx(gate.params[0], t0)
            elif name == "ry":    qk.ry(gate.params[0], t0)
            elif name == "rz":    qk.rz(gate.params[0], t0)
            elif name == "cx":    qk.cx(gate.controls[0], t0)
            elif name == "cz":    qk.cz(gate.controls[0], t0)
            elif name == "swap":  qk.swap(gate.targets[0], gate.targets[1])
            elif name == "ccx":   qk.ccx(gate.controls[0], gate.controls[1], t0)
            elif name == "mz":    measure_targets.append(t0)
            else:
                raise ValueError(f"IonQBackend does not support gate: {name!r}")

        for t in measure_targets:
            qk.measure(t, t)
        return qk

run(circuit, shots=1000, include_statevector=False, **kwargs)

Translate, transpile and execute circuit on the IonQ backend.

Parameters

circuit : QuantumCircuit The HLQuantum circuit to execute. shots : int, optional Number of measurement shots (default 1000). include_statevector : bool, optional If True, attempt to retrieve the state vector. Note that state-vector retrieval is only supported on simulators. **kwargs Forwarded to the underlying Qiskit backend.run() call.

Source code in hlquantum/backends/ionq_backend.py
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def run(
    self,
    circuit: QuantumCircuit,
    shots: int = 1000,
    include_statevector: bool = False,
    **kwargs: Any,
) -> ExecutionResult:
    """Translate, transpile and execute *circuit* on the IonQ backend.

    Parameters
    ----------
    circuit : QuantumCircuit
        The HLQuantum circuit to execute.
    shots : int, optional
        Number of measurement shots (default 1000).
    include_statevector : bool, optional
        If *True*, attempt to retrieve the state vector.  Note that
        state-vector retrieval is only supported on simulators.
    **kwargs
        Forwarded to the underlying Qiskit ``backend.run()`` call.
    """
    qiskit = _require_qiskit()
    qk_circuit = self._translate(circuit, qiskit)

    # Resolve backend
    qk_backend = self._resolve_backend()

    # Optional: statevector save instruction (simulator only)
    if include_statevector:
        try:
            qk_circuit.save_statevector()
        except Exception:
            logger.warning(
                "State-vector save is not supported on this IonQ backend; "
                "the result will not contain a state vector."
            )

    # Transpile
    if self._transpile:
        from qiskit import transpile as qk_transpile  # type: ignore[import-untyped]
        qk_circuit = qk_transpile(
            qk_circuit,
            backend=qk_backend,
            optimization_level=self._optimization_level,
        )

    logger.info(
        "Running %d-qubit circuit (%d gates) for %d shots on %s (SV: %s)",
        circuit.num_qubits,
        len(circuit),
        shots,
        self.name,
        include_statevector,
    )

    actual_shots = max(shots, 1)
    job = qk_backend.run(qk_circuit, shots=actual_shots, **kwargs)
    raw_result = job.result()

    # --- Counts --------------------------------------------------------
    counts: Dict[str, int] = {}
    if shots > 0:
        try:
            raw_counts = raw_result.get_counts()
            if isinstance(raw_counts, list):
                raw_counts = raw_counts[0]
            for bitstring, count in raw_counts.items():
                counts[bitstring.replace(" ", "")] = count
        except Exception:
            logger.warning("Could not extract measurement counts from the IonQ result.")

    # --- Statevector ---------------------------------------------------
    state_vector = None
    if include_statevector:
        try:
            state_vector = raw_result.get_statevector()
        except Exception:
            logger.warning("Failed to retrieve state vector from result.")

    return ExecutionResult(
        counts=counts,
        shots=shots,
        backend_name=self.name,
        raw=raw_result,
        state_vector=state_vector,
        metadata={"ionq_backend": self._backend_name},
    )