Source code for openqaoa.backends.qaoa_backend

from __future__ import annotations
from typing import Union, Optional, List
import numpy as np

from .plugin_finder import PLUGIN_DICT
from .qaoa_vectorized import QAOAvectorizedBackendSimulator
from .qaoa_analytical_sim import QAOABackendAnalyticalSimulator
from .devices_core import DeviceBase, DeviceLocal
from .basebackend import QuantumCircuitBase, QAOABaseBackend
from ..qaoa_components import QAOADescriptor


def _create_mappers(input_plugin_dict: dict) -> dict:
    DEVICE_NAME_TO_OBJECT_MAPPER = dict()
    DEVICE_ACCESS_OBJECT_MAPPER = dict()

    DEVICE_NAME_TO_OBJECT_MAPPER["vectorized"] = QAOAvectorizedBackendSimulator
    DEVICE_NAME_TO_OBJECT_MAPPER[
        "analytical_simulator"
    ] = QAOABackendAnalyticalSimulator

    for each_entry_key, each_entry_value in input_plugin_dict.items():
        if hasattr(each_entry_value, "device_access"):
            DEVICE_ACCESS_OBJECT_MAPPER.update(each_entry_value.device_access)
        if hasattr(each_entry_value, "device_name_to_obj"):
            DEVICE_NAME_TO_OBJECT_MAPPER.update(each_entry_value.device_name_to_obj)

    return DEVICE_NAME_TO_OBJECT_MAPPER, DEVICE_ACCESS_OBJECT_MAPPER


DEVICE_NAME_TO_OBJECT_MAPPER, DEVICE_ACCESS_OBJECT_MAPPER = _create_mappers(PLUGIN_DICT)


def _backend_arg_mapper(
    backend_obj: QAOABaseBackend,
    n_shots: Optional[int] = None,
    seed_simulator: Optional[int] = None,
    qiskit_simulation_method: Optional[str] = None,
    qiskit_optimization_level: Optional[int] = None,
    noise_model=None,
    active_reset: Optional[bool] = None,
    rewiring=None,
    disable_qubit_rewiring: Optional[bool] = None,
    initial_qubit_mapping=None,
):
    BACKEND_ARGS_MAPPER = {
        QAOABackendAnalyticalSimulator: {},
        QAOAvectorizedBackendSimulator: {},
    }

    local_vars = locals()

    for each_plugin_entrypoint in PLUGIN_DICT.values():
        if hasattr(each_plugin_entrypoint, "backend_args"):
            for each_key, each_value in each_plugin_entrypoint.backend_args.items():
                # Convert list of accepted parameters into a dictionary with
                # the name of the variable as a key and the local value of the
                # variable
                var_values = [local_vars[each_name] for each_name in each_value]
                input_dict = {each_key: dict(zip(each_value, var_values))}
                BACKEND_ARGS_MAPPER.update(input_dict)

    final_backend_kwargs = {
        key: value
        for key, value in BACKEND_ARGS_MAPPER[backend_obj].items()
        if value is not None
    }
    return final_backend_kwargs


[docs]def device_to_backend_mapper(device: DeviceBase) -> QAOABaseBackend: """ Return the correct `QAOABaseBackend` object corresponding to the requested device """ if isinstance(device, DeviceLocal): try: backend_class = DEVICE_NAME_TO_OBJECT_MAPPER[device.device_name] except KeyError: raise ValueError( f"The device {device} is not supported." f"Please choose from {DEVICE_NAME_TO_OBJECT_MAPPER.keys()}" ) except: raise Exception(f"The device name {device} raised an unknown error") else: try: backend_class = DEVICE_ACCESS_OBJECT_MAPPER[type(device)] except KeyError: raise ValueError( f"The device {device} is not supported." f"Please choose from {DEVICE_ACCESS_OBJECT_MAPPER.keys()}" ) except: raise Exception(f"The specified {device} raised an unknown error") return backend_class
[docs]def get_qaoa_backend( qaoa_descriptor: QAOADescriptor, device: DeviceBase, prepend_state: Optional[ Union[QuantumCircuitBase, List[complex], np.ndarray] ] = None, append_state: Optional[Union[QuantumCircuitBase, np.ndarray]] = None, init_hadamard: bool = True, cvar_alpha: float = 1, **kwargs, ): """ A wrapper function to return a QAOA backend object. Parameters ---------- qaoa_descriptor: `QAOADescriptor` An object of the class ``QAOADescriptor`` which contains information on circuit construction along with depth of the circuit. device: `DeviceBase` The device to be used: Specified as an object of the class `DeviceBase`. prepend_state: `Union[QuantumCircuitBase,np.ndarray(complex)]` The state prepended to the circuit. append_state: `Union[QuantumCircuitBase,np.ndarray(complex)]` The state appended to the circuit. init_hadamard: `bool` Whether to apply a Hadamard gate to the beginning of the QAOA part of the circuit. cvar_alpha: `float` The value of the CVaR parameter. kwargs: Additional keyword arguments for the backend. initial_qubit_mapping: `list` A list of physical qubits to be used for the QAOA circuit. n_shots: `int` The number of shots to be used for the shot-based computation. Returns ------- `QAOABaseBackend` The corresponding backend object. """ backend_class = device_to_backend_mapper(device) backend_kwargs = _backend_arg_mapper(backend_class, **kwargs) try: if isinstance(device, DeviceLocal): backend_obj = backend_class( qaoa_descriptor=qaoa_descriptor, prepend_state=prepend_state, append_state=append_state, init_hadamard=init_hadamard, cvar_alpha=cvar_alpha, **backend_kwargs, ) else: backend_obj = backend_class( qaoa_descriptor=qaoa_descriptor, device=device, prepend_state=prepend_state, append_state=append_state, init_hadamard=init_hadamard, cvar_alpha=cvar_alpha, **backend_kwargs, ) except Exception as e: raise ValueError(f"The backend returned an error: {e}") return backend_obj