"""
********************************************************************************
* Copyright (c) 2026 the Qrisp authors
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************
"""
from uuid import UUID
from qrisp.interface import BatchedBackend
[docs]
def IQMBackend(
api_token,
device_instance=None,
server_url=None,
compilation_options=None,
transpiler=None,
calibration_set_id: str | UUID | None = None,
use_metrics: bool = False,
use_timeslot: bool = False,
):
"""
This function creates a :ref:`BatchedBackend` for executing circuits on IQM hardware.
Parameters
----------
api_token : str
An API token retrieved from the IQM Resonance website or IQM backend.
device_instance : str
The device instance of the IQM backend such as ``garnet``.
For an up-to-date list, see the IQM Resonance website.
Required if server_url is not provided.
server_url : str, optional
The server URL of the IQM backend. If not provided, it defaults to IQM resonance
using the device_instance. If a server URL is provided, a device instance should not be provided.
compilation_options: CircuitCompilationOptions
An object to specify several options regarding `pulse-level compilation <https://docs.meetiqm.com/iqm-client/api/iqm.iqm_client.models.CircuitCompilationOptions.html>`_.
transpiler : callable, optional
A function receiving and returning a QuantumCircuit, mapping the given
circuit to a hardware friendly circuit. By default the `transpile_to_iqm <https://iqm-finland.github.io/qiskit-on-iqm/api/iqm.qiskit_iqm.iqm_naive_move_pass.transpile_to_IQM.html>`_
function will be used.
calibration_set_id: ID of the calibration set the backend will use.
``None`` means the IQM Server will be queried for the current default
calibration set.
use_metrics: If True, the backend will query the server for calibration data and related
quality metrics, and pass these to the transpilation target(s). The default value is set
to False until quality metrics become available on the Resonance API.
Examples
--------
We evaluate a :ref:`QuantumFloat` multiplication on the 20-qubit IQM Garnet.
>>> from qrisp.interface import IQMBackend
>>> qrisp_garnet = IQMBackend(api_token = "YOUR_IQM_RESONANCE_TOKEN", device_instance = "garnet")
>>> from qrisp import QuantumFloat
>>> a = QuantumFloat(2)
>>> a[:] = 2
>>> b = a*a
>>> b.get_measurement(backend = qrisp_garnet, shots = 1000)
{4: 0.548,
5: 0.082,
0: 0.063,
6: 0.042,
8: 0.031,
2: 0.029,
12: 0.014,
10: 0.03,
1: 0.027,
7: 0.025,
15: 0.023,
9: 0.021,
14: 0.021,
13: 0.018,
11: 0.014,
3: 0.012}
**Manual qubit selection and routing**
In the next example we showcase how to prevent automatic selection of qubits.
For this we overide the transpilation procedure. The default transpilation
calls the ``transpile_to_IQM`` function, which performs routing,
automatic selection of suitable qubits, basis-gate transformation and other
optimizations.
For our example we will just transform to the required basis gates and ensure
manually that our circuit has the correct connectivity.
::
from qrisp import QuantumCircuit
# The custom transpiler should receive and return a QuantumCircuit
def custom_transpiler(qc: QuantumCircuit) -> QuantumCircuit:
return qc.transpile(basis_gates = ["cz", "r", "measure", "reset"])
custom_transpiled_garnet = IQMBackend("YOUR_IQM_RESONANCE_TOKEN",
device_instance = "garnet",
transpiler = custom_transpiler)
# Create a bell state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure(0)
# execute
meas_res = qc.run(shots = 10000, backend = custom_transpiled_garnet)
"""
if not isinstance(api_token, str):
raise TypeError(
"api_token must be a string. You can create an API token on the IQM Resonance website."
)
# Validate that either server_url or device_instance is provided, but not both
if server_url is not None and device_instance is not None:
raise ValueError(
"Please provide either a server_url or a device_instance, but not both."
)
if server_url is None and device_instance is None:
raise ValueError("Please provide either a server_url or a device_instance.")
if device_instance is not None and not isinstance(device_instance, str):
raise TypeError(
"device_instance must be a string. You can retrieve a list of available devices on the IQM Resonance website."
)
if server_url is not None and not isinstance(server_url, str):
raise TypeError("server_url must be a string.")
try:
from iqm.iqm_client import CircuitCompilationOptions
from iqm.iqm_client.iqm_client import IQMClient
from iqm.qiskit_iqm import transpile_to_IQM
from iqm.qiskit_iqm.iqm_provider import IQMBackend
except ImportError:
raise ImportError(
"Please install qiskit-iqm to use the IQMBackend. You can do this by running `pip install qrisp[iqm]`."
)
# Construct the server URL based on device_instance if server_url is not provided
if server_url is None:
server_url = "https://resonance.meetiqm.com/"
client = IQMClient(
iqm_server_url=server_url, token=api_token, quantum_computer=device_instance
)
backend = IQMBackend(
client, calibration_set_id=calibration_set_id, use_metrics=use_metrics
)
if compilation_options is None:
compilation_options = CircuitCompilationOptions()
if transpiler is None:
def transpiler(qc):
from qrisp import QuantumCircuit
qiskit_qc = qc.to_qiskit()
transpiled_qiskit_qc = transpile_to_IQM(qiskit_qc, backend)
return QuantumCircuit.from_qiskit(transpiled_qiskit_qc)
def run_batch_iqm(batch):
circuit_batch = []
shot_batch = []
for qc, shots in batch:
if device_instance == "sirius":
qiskit_qc = transpile_to_IQM(qc.to_qiskit(), backend)
else:
transpiled_qc = transpiler(qc)
qiskit_qc = transpiled_qc.to_qiskit()
circuit_batch.append(backend.serialize_circuit(qiskit_qc))
if shots is None:
shots = 1000
shot_batch.append(shots)
job = client.submit_circuits(
circuit_batch,
options=compilation_options,
shots=max(shot_batch),
use_timeslot=use_timeslot,
)
job.wait_for_completion()
answer = job.result()
counts_batch = []
for i in range(len(batch)):
counts = answer[i]
counts_dic = {}
shots = batch[i][1]
if shots is None:
shots = 1000
for j in range(shots):
key_str = ""
for k in counts.keys():
key_str += str(counts[k][j][0])
if key_str in counts_dic:
counts_dic[key_str] += 1
else:
counts_dic[key_str] = 1
counts_batch.append(counts_dic)
return counts_batch
return BatchedBackend(run_batch_iqm)