frompprintimportPrettyPrinterfromtypingimportAny,List,Unionfromcompute_api_clientimportApiClient,BackendStatus,BackendType,BackendTypesApifromqi2_shared.clientimportconfigfromqi2_shared.utilsimportrun_asyncfromqiskit.circuitimportQuantumCircuitfromqiskit.providersimportBackendV2asBackendfromqiskit.providers.optionsimportOptionsfromqiskit.transpilerimportCouplingMap,Targetfromqiskit_quantuminspire.mapping.instruction_mappingimportInstructionMappingfromqiskit_quantuminspire.qi_instructionsimportAsmfromqiskit_quantuminspire.qi_jobsimportQIJobfromqiskit_quantuminspire.utilsimportis_coupling_map_complete_IGNORED_GATES:set[str]={# Prep not viewed as separate gates in Qiskit"prep_x","prep_y","prep_z",# Measure x and y not natively supported https://github.com/Qiskit/qiskit/issues/3967"measure_x","measure_y","measure_all",# Measure z is equivalent to measure"measure_z",# May be supportable through parameterized CPhaseGate.# For now, direct usage of CPhaseGate is required"crk",# No direct qiskit equivalent"x90","mx90","y90","my90",# Qiskit assumes barrier support and does not include it in its standard gate mapping"barrier",}# Ignore type checking for QIBackend due to missing Qiskit type stubs,# which causes the base class 'Backend' to be treated as 'Any'.
[docs]classQIBaseBackend(Backend):# type: ignore[misc]_max_shots:intdef__init__(self,backend_type:BackendType,mapping:InstructionMapping=InstructionMapping(),**kwargs:Any):super().__init__(name=backend_type.name,description=backend_type.description,**kwargs)self._id:int=backend_type.idself._max_shots:int=backend_type.max_number_of_shots# Construct optionsself._options=self._default_options()self.set_options(shots=backend_type.default_number_of_shots)ifnotbackend_type.supports_raw_data:self._options.set_validator("memory",[False])# Determine supported gatesopensquirrel_gates={inst.lower()forinstinmapping.supported_opensquirrel_instructions()}available_gates=opensquirrel_gates-_IGNORED_GATES# Construct coupling mapcoupling_map=CouplingMap(backend_type.topology)coupling_map_complete=is_coupling_map_complete(coupling_map)if"toffoli"inavailable_gatesandnotcoupling_map_complete:# "Toffoli gate not supported for non-complete topologyavailable_gates.remove("toffoli")self._target=Target().from_configuration(basis_gates=[mapping.opensquirrel_to_qiskit(gate)forgateinavailable_gates],num_qubits=backend_type.nqubits,coupling_map=Noneifcoupling_map_completeelsecoupling_map,)self._target.add_instruction(Asm())def__repr_pretty__(self,p:PrettyPrinter)->None:p.pprint(f"QIBackend(name={self.name}, id={self.id})")def__repr__(self)->str:module_name=self.__class__.__module__s=f"<{module_name}.{self.__class__.__name__} object at 0x{id(self):x} (name={self.name}, id={self.id})>"returns@classmethoddef_default_options(cls)->Options:"""Only options defined here are supported by the backend. shots: int: Number of shots for the job. """options=Options(shots=1024,seed_simulator=None,memory=False)# Seed_simulator is included in options to enable use of BackendEstimatorV2 in Qiskit,# but is not actually supported by the backend so any other value than none raises an error.options.set_validator("seed_simulator",[None])options.set_validator("shots",int)options.set_validator("memory",bool)returnoptions@propertydeftarget(self)->Target:returnself._target@propertydefmax_shots(self)->int:returnself._max_shots@propertydefmax_circuits(self)->Union[int,None]:returnNone@propertydefid(self)->int:returnself._id
[docs]classQIBackend(QIBaseBackend):"""A wrapper class for QuantumInspire backendtypes to integrate with Qiskit's Backend interface."""@propertydefstatus(self)->BackendStatus:backend_type:BackendType=run_async(self._get_backend_type())returnbackend_type.statusasyncdef_get_backend_type(self)->BackendType:asyncwithApiClient(config())asclient:backend_types_api=BackendTypesApi(client)returnawaitbackend_types_api.read_backend_type_backend_types_id_get(self._id)@propertydefavailable(self)->bool:returnbool(self.status!=BackendStatus.OFFLINE)
[docs]defrun(self,run_input:Union[QuantumCircuit,List[QuantumCircuit]],**options:Any)->QIJob:"""Create and run a (batch)job on an QuantumInspire Backend. Args: run_input: A single or list of Qiskit QuantumCircuit objects or hybrid algorithms. Returns: QIJob: A reference to the batch job that was submitted. """ifnotself.available:raiseRuntimeError(f"{self.name} is {self.status.value}, jobs can't be submitted")self.set_options(**options)job=QIJob(run_input=run_input,backend=self)job.submit()returnjob