Source code for qbraid.runtime.ionq.provider

# Copyright (C) 2024 qBraid
#
# This file is part of the qBraid-SDK
#
# The qBraid-SDK is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.

"""
Module defining IonQ session and provider classes

"""
import os
from typing import Any, Optional

from qbraid_core.sessions import Session

from qbraid._caching import cached_method
from qbraid._version import __version__ as qbraid_version
from qbraid.programs import ExperimentType, ProgramSpec
from qbraid.programs.gate_model.ionq import (
    IONQ_NATIVE_GATES,
    IONQ_NATIVE_GATES_BASE,
    IONQ_NATIVE_GATES_FAMILY,
    IONQ_QIS_GATES,
)
from qbraid.runtime.exceptions import ResourceNotFoundError
from qbraid.runtime.noise import NoiseModelSet
from qbraid.runtime.profile import TargetProfile
from qbraid.runtime.provider import QuantumProvider
from qbraid.transpiler.conversions.qasm2.qasm2_to_ionq import qasm2_to_ionq
from qbraid.transpiler.conversions.qasm3.qasm3_to_ionq import qasm3_to_ionq

from .device import IonQDevice


[docs] class IonQSession(Session): """IonQ session class."""
[docs] def __init__(self, api_key: Optional[str] = None): api_key = api_key or os.getenv("IONQ_API_KEY") if not api_key: raise ValueError( "An IonQ API key is required to initialize the session. " "Please provide it directly as an argument or set it via " "the IONQ_API_KEY environment variable." ) super().__init__( base_url="https://api.ionq.co/v0.3", headers={"Content-Type": "application/json"}, auth_headers={"Authorization": f"apiKey {api_key}"}, ) self.api_key = api_key self.add_user_agent(f"QbraidSDK/{qbraid_version}")
def get_devices(self, **kwargs) -> dict[str, dict[str, Any]]: """Get all IonQ devices.""" devices_list = self.get("/backends", **kwargs).json() devices_dict = {device["backend"]: device for device in devices_list} return devices_dict def get_device(self, device_id: str) -> dict[str, Any]: """Get a specific IonQ device.""" devices = self.get_devices() try: return devices[device_id] except KeyError as err: raise ResourceNotFoundError(f"Device '{device_id}' not found.") from err def create_job(self, data: dict[str, Any]) -> dict[str, Any]: """Create a new job on the IonQ API.""" return self.post("/jobs", data=data).json() def get_job(self, job_id: str) -> dict[str, Any]: """Get a specific IonQ job.""" return self.get(f"/jobs/{job_id}").json() def cancel_job(self, job_id: str): """Cancel a specific IonQ job.""" return self.put(f"/jobs/{job_id}/status/cancel").json()
[docs] class IonQProvider(QuantumProvider): """IonQ provider class."""
[docs] def __init__(self, api_key: Optional[str] = None): self.session = IonQSession(api_key)
def _get_characterization(self, data: dict[str, Any]) -> Optional[dict[str, Any]]: """Return the characterization of the IonQ device.""" characterization_url = data.get("characterization_url") if not characterization_url: return None characterization_endpoint = f"{self.session.base_url}{characterization_url}" return self.session.get(characterization_endpoint).json() @staticmethod def _get_basis_gates(device_id: str) -> list[str]: """Return the basis gates for the IonQ device.""" if device_id == "simulator": native_gates = IONQ_NATIVE_GATES.copy() else: native_gates = next( (gates for key, gates in IONQ_NATIVE_GATES_FAMILY.items() if key in device_id), IONQ_NATIVE_GATES_BASE, ) basis_gates = IONQ_QIS_GATES.copy() + native_gates return basis_gates def _build_profile(self, data: dict[str, Any]) -> TargetProfile: """Build a profile for an IonQ device.""" device_id = data.get("backend") simulator = device_id == "simulator" charact = self._get_characterization(data) basis_gates = self._get_basis_gates(device_id) noise_models = ( NoiseModelSet.from_iterable(data.get("noise_models", [])) if simulator else None ) return TargetProfile( device_id=device_id, simulator=simulator, experiment_type=ExperimentType.GATE_MODEL, num_qubits=data.get("qubits"), program_spec=[ ProgramSpec(str, alias="qasm2", to_ir=qasm2_to_ionq), ProgramSpec(str, alias="qasm3", to_ir=qasm3_to_ionq), ], provider_name="IonQ", basis_gates=basis_gates, characterization=charact, noise_models=noise_models, ) @cached_method def get_device( self, device_id: str, ) -> dict[str, Any]: """Get a specific IonQ device.""" data = self.session.get_device(device_id) profile = self._build_profile(data) return IonQDevice(profile, self.session) @cached_method def get_devices(self, **kwargs) -> list[IonQDevice]: """Get a list of IonQ devices.""" devices = self.session.get_devices(**kwargs) return [ IonQDevice(self._build_profile(device), self.session) for device in devices.values() ] def __hash__(self): if not hasattr(self, "_hash"): object.__setattr__(self, "_hash", hash((self.session.api_key, self.session.base_url))) return self._hash # pylint: disable=no-member