Skip to main content
IBM Quantum Platform

제공자 인터페이스

qiskit.providers

이 모듈에는 Qiskit용 외부 프로바이더를 구축하는 데 사용되는 클래스들이 포함되어 있습니다. 프로바이더란 Qiskit에 외부 서비스를 제공하는 모든 것을 말합니다. 이에 대한 대표적인 예로는, 객체를 실행하는 QuantumCircuit 데 사용할 수 있는 객체를 제공하는 Backend 백엔드 제공자가 있습니다. 이 모듈에는 프로바이더와 Qiskit 간의 인터페이스를 정의하는 데 사용되는 추상 클래스들이 포함되어 있습니다.


버전 지원

각 공급자 인터페이스 추상 클래스는 개별적으로 버전이 지정됩니다. 인터페이스를 변경해야 하는 경우 새 인터페이스를 정의하기 위해 새 추상 클래스가 생성됩니다. 이러한 인터페이스 변경 사항은 버전 간 하위 호환이 보장되지 않습니다.

버전 변경

qiskit 의 각 마이너 버전 릴리즈는 백엔드 인터페이스의 버전을 한 버전씩 증가시킬 수 있습니다. 해당 인터페이스의 해당 릴리스에 대한 모든 인터페이스 변경 사항을 집계한 것입니다.

버전 지원 정책

공급자가 이 인터페이스의 변경 사항에 적응할 시간을 갖도록 하기 위해 키스킷은 각 클래스의 여러 버전을 한 번에 지원합니다. 릴리스당 하나의 버전이라는 특성을 고려할 때 버전 사용 중단 정책은 표준 사용 중단 정책보다 조금 더 보수적입니다. 키스킷은 잠재적인 지원 중단 이전에 최소 3개의 마이너 릴리스 또는 버전을 도입한 릴리스에서 6개월 후 첫 번째 릴리스 중 더 긴 기간 동안 공급자 인터페이스 버전을 지원할 것입니다. 그 이후에는 표준 지원 중단 정책이 해당 인터페이스 버전에 적용됩니다. 이렇게 하면 공급자와 사용자가 인터페이스의 잠재적인 획기적인 변화에 적응할 수 있는 충분한 시간을 확보할 수 있습니다. 예를 들어 0.19.0 BackendV2 이 소개되고 3개월 후 0.19.0, 0.20.0, 0.21.0, 0.22.0 을 출시하고 7개월 후 0.19.0 을 출시하고 0.23.0 을 출시한다고 가정해 보겠습니다. 0.23.0 에서 BackendV2 을 사용 중단할 수 있으며, 사용 중단 정책이 완료될 때까지 계속 지원되어야 하고 제거할 수 없습니다.

키스킷의 버전 지원 정책은 제공자 자체에 동일한 지원 스토리가 적용된다는 것을 의미하지 않으며, 제공자는 가능한 한 빨리 최신 버전으로 업데이트할 수 있으며, 지원 기간은 키스킷이 지원하는 버전에 대해서만 적용된다는 점을 지적할 필요가 있습니다. 사용 중단에 앞서 이렇게 긴 기간을 두는 이유는 제공업체가 버전을 변경하기 전에 최종 사용자에게 영향을 미칠 수 있는 인터페이스의 일부 변경 사항에 대해 자체적으로 사용 중단을 수행할 수 있는 충분한 시간을 주기 위함입니다. 예를 들어 이전 버전과 호환되지 않는 방식으로 Backend.run() 에서 BackendV34 으로 서명을 변경했다고 가정해 보겠습니다. 에어가 버전 34를 기반으로 클래스를 업데이트하기 전에 AerSimulator 클래스를 버전 34를 기반으로 업데이트하려면 먼저 이전 서명을 더 이상 사용하지 않아야 합니다. 에어에 대한 전환이 키스킷과 일치한다고 보장할 수 없으므로, 버전 33을 제거하기 전에 에어가 지원 중단 주기를 완료할 수 있는 충분한 시간이 필요합니다(즉, 버전 34를 필수/최소 버전으로 만드는 것).


추상 클래스

백엔드

칼럼 1
칼럼 2
Backend()모든 버전의 백엔드 추상 클래스에 대한 기본 공통 유형입니다.
BackendV2( [제공자, 이름, 설명,...] )백엔드용 추상 클래스
QubitProperties( [t1, t2, frequency] )QubitProperties 객체의 표현입니다.

옵션

칼럼 1
칼럼 2
Options(**kwargs)기본 옵션 개체

작업

칼럼 1
칼럼 2
Job()모든 버전의 Job 추상 클래스에 대한 기본 공통 유형입니다.
JobV1(백엔드, job_id, **kwargs)작업을 처리하는 클래스

작업 상태

칼럼 1
칼럼 2
JobStatus(*값)작업 상태 열거형에 대한 클래스입니다.

예외

QiskitBackendNotFoundError

exception qiskit.providers.QiskitBackendNotFoundError(*message)

GitHub

베이스: QiskitError

백엔드를 찾는 동안 발생하는 오류에 대한 베이스 클래스입니다.

오류 메시지를 설정합니다.

JobError

exception qiskit.providers.JobError(*message)

GitHub

베이스: QiskitError

잡에서 발생하는 오류에 대한 베이스 클래스입니다.

오류 메시지를 설정합니다.

JobTimeoutError

exception qiskit.providers.JobTimeoutError(*message)

GitHub

베이스: JobError

작업에서 발생하는 시간 초과 오류에 대한 베이스 클래스입니다.

오류 메시지를 설정합니다.


새로운 백엔드 작성하기

Qiskit과 연동하고 싶은 양자 장치나 시뮬레이터가 있다면 백엔드를 직접 작성해야 합니다. 프로바이더는 백엔드들의 집합이며, Qiskit에 사용 가능한 BackendV2 객체를 가져올 수 있는 방법을 제공합니다. 이 BackendV2 객체는 백엔드를 설명하는 정보와 해당 백엔드에서 작동하는 방식을 transpiler 모두 제공하므로, 회로를 최적화된 형태로 컴파일하여 백엔드에서 실행할 수 있게 합니다. 또한 객체를 QuantumCircuit 실행할 수 있는 메서드도 run() 제공합니다. 이를 통해 사용자와 다른 Qiskit API는 백엔드가 어떻게 구현되었는지와 관계없이 표준화된 방식으로 디바이스에서 회로를 실행한 결과를 얻을 수 있습니다. 대략적으로 볼 때, 프로바이더를 작성하는 기본 단계는 다음과 같습니다:

  • 백엔드에 대한 액세스를 처리하는 Provider 클래스를 구현합니다.

  • 하위 BackendV2 클래스와 그 run() 메서드를 구현하십시오.

    • 백엔드의 기초에 대한 사용자 정의 게이트를 세션 EquivalenceLibrary 인스턴스에 추가합니다.
  • 실행 중인 작업과의 상호작용을 처리하는 하위 클래스를 JobV1 구현하십시오.

공급자의 간단한 예는 qiskit-aqt-provider를 참고한다

제공자

공급자 클래스는 디바이스나 시뮬레이터에서 회로를 실행할 수 있는 백엔드 객체를 가져오는 단일 용도로 사용됩니다. 필요한 자격 증명 및/또는 인증은 공급자 개체를 초기화할 때 처리될 것으로 예상됩니다. 그러면 공급자 개체는 백엔드 목록과 백엔드를 필터링하고 획득하는 메서드를 제공합니다(필요한 경우 제공된 자격 증명을 사용). 예제 공급자 클래스는 다음과 같습니다:

from qiskit.providers.providerutils import filter_backends

from .backend import MyBackend

class MyProvider:

    def __init__(self, token=None):
        super().__init__()
        self.token = token
        self.backends = [MyBackend(provider=self)]

    def backends(self, name=None, **kwargs):
        if name:
            backends = [
                backend for backend in backends if backend.name() == name]
        return filter_backends(backends, filters=filters, **kwargs)

인증에 필요한 정보(필요한 경우)가 클래스에 포함되어 있는지, 그리고 백엔드의 메서드가 요구되는 인터페이스와 일치하는지 확인하십시오. 나머지는 구체적인 구현 방식에 따라 각 제공업체의 재량에 달려 있습니다.

백엔드

백엔드 클래스는 프로바이더의 핵심입니다. 이 클래스들은 Qiskit과 회로를 실행할 하드웨어 또는 시뮬레이터 간의 인터페이스를 제공하는 역할을 합니다. 여기에는 컴파일러가 백엔드에 대한 회로를 임베드하고 최적화할 수 있도록, 해당 백엔드를 설명하는 데 필요한 정보를 제공하는 것이 포함됩니다. 모든 백엔드 객체에는 4가지 필수 요소가 있습니다: 컴파일러에 백엔드의 모델을 정의하는 속성 target , 백엔드가 단일 배치 작업에서 실행할 수 있는 회로 수에 대한 제한을 정의하는 속성 max_circuits (제한이 None 없는 경우 ``를 사용할 수 있음), 작업 제출을 수락하는 메서드 run() , 그리고 사용자가 구성할 수 있는 옵션과 해당 기본값을 정의하는 메서드입니다 _default_options . 예를 들어, 가장 간단한 작동 예제는 다음과 같을 것입니다:

from qiskit.providers import BackendV2 as Backend
from qiskit.transpiler import Target
from qiskit.providers import Options
from qiskit.circuit import Parameter, Measure
from qiskit.circuit.library import PhaseGate, SXGate, UGate, CXGate, IGate


class Mybackend(Backend):

    def __init__(self):
        super().__init__()

        # Create Target
        self._target = Target("Target for My Backend")
        # Instead of None for this and below instructions you can define
        # a qiskit.transpiler.InstructionProperties object to define properties
        # for an instruction.
        lam = Parameter("λ")
        p_props = {(qubit,): None for qubit in range(5)}
        self._target.add_instruction(PhaseGate(lam), p_props)
        sx_props = {(qubit,): None for qubit in range(5)}
        self._target.add_instruction(SXGate(), sx_props)
        phi = Parameter("φ")
        theta = Parameter("ϴ")
        u_props = {(qubit,): None for qubit in range(5)}
        self._target.add_instruction(UGate(theta, phi, lam), u_props)
        cx_props = {edge: None for edge in [(0, 1), (1, 2), (2, 3), (3, 4)]}
        self._target.add_instruction(CXGate(), cx_props)
        meas_props = {(qubit,): None for qubit in range(5)}
        self._target.add_instruction(Measure(), meas_props)
        id_props = {(qubit,): None for qubit in range(5)}
        self._target.add_instruction(IGate(), id_props)

        # Set option validators
        self.options.set_validator("shots", (1, 4096))
        self.options.set_validator("memory", bool)

    @property
    def target(self):
        return self._target

    @property
    def max_circuits(self):
        return 1024

    @classmethod
    def _default_options(cls):
        return Options(shots=1024, memory=False)

    def run(self, circuits, **kwargs):
        # serialize circuits submit to backend and create a job
        for kwarg in kwargs:
            if not hasattr(self.options, kwarg):
                warnings.warn(
                    "Option %s is not used by this backend" % kwarg,
                    UserWarning, stacklevel=2)
        options = {
            'shots': kwargs.get('shots', self.options.shots),
            'memory': kwargs.get('memory', self.options.memory),
        }
        job_json = convert_to_wire_format(circuit, options)
        job_handle = submit_to_backend(job_json)
        return MyJob(self.job_handle, job_json, circuit)

백엔드의 트랜스파일러 인터페이스

이 Backend 객체의 핵심은 컴파일러에 대해 스스로를 어떻게 설명하느냐에 있습니다. 이는 트랜스파일러의 백엔드 모델을 정의하는 클래스를 Target 통해 처리됩니다. 백엔드 객체는 속성에서 target 객체를 Target 반환해야 하며, 함수는 transpile() 이를 컴파일을 위한 백엔드 대상의 모델로 사용합니다.

맞춤형 베이시스 게이트

  1. 백엔드에서 Qiskit 회로 라이브러리(qiskit.circuit.library)의 게이트를 사용하지 않는 경우, 해당 기능을 프로바이더에 통합할 수 있습니다. 이를 수행하는 기본적인 방법은 먼저 기저 집합에 포함된 각 사용자 정의 게이트에 대해 하위 Gate 클래스를 정의하는 것입니다. 예를 들어,

    import numpy as np
    
    from qiskit.circuit import Gate
    from qiskit.circuit import QuantumCircuit
    
    class SYGate(Gate):
        def __init__(self, label=None):
            super().__init__("sy", 1, [], label=label)
    
        def _define(self):
            qc = QuantumCircuit(1)
            qc.ry(np.pi / 2, 0)
            self.definition = qc

    확인해야 할 핵심 사항은 백엔드에서 사용자 정의 게이트의 이름 속성(위의 __init__ 정의에서 super().__init__() 의 첫 번째 파라미터)이 다른 게이트의 이름과 충돌하지 않도록 설정하는 것입니다. 이름 속성은 트랜스파일러의 기준 세트에서 게이트를 식별하는 데 사용되는 속성입니다. 충돌이 발생하면 트랜스파일러는 어떤 게이트를 사용할지 알 수 없습니다.

  2. 백엔드의 대상에 사용자 정의 게이트를 추가하세요. 이는 메서드를 Target.add_instruction() 사용하여 수행할 수 있습니다. 트랜스파일러가 해당 인스턴스의 존재를 인식할 수 있도록, 대상에 해당 SYGate 인스턴스와 그 매개변수를 추가해야 합니다. 예를 들어, 이것이 백엔드 구현의 BackendV2 일부라고 가정해 보겠습니다:

    from qiskit.transpiler import InstructionProperties
    
    sy_props = {
        (0,): InstructionProperties(duration=2.3e-6, error=0.0002),
        (1,): InstructionProperties(duration=2.1e-6, error=0.0001),
        (2,): InstructionProperties(duration=2.5e-6, error=0.0003),
        (3,): InstructionProperties(duration=2.2e-6, error=0.0004),
    }
    self.target.add_instruction(SYGate(), sy_props)

    sy_props 의 키는 백엔드 SYGate 를 사용할 수 있는 큐비트를 정의하고, 값은 해당 큐비트에 대한 SYGate 의 속성을 정의합니다. 멀티큐비트 게이트의 경우 튜플 키에는 게이트가 작동하는 모든 큐비트 조합이 포함됩니다(순서가 중요합니다. 즉, 순서가 중요합니다. (0, 1) 는 (1, 0))와 다릅니다.

  3. 백엔드의 기저 집합에 사용할 사용자 정의 게이트를 정의한 후에는, 함수와 transpiler 모듈이 transpile() 사용자 정의 기저 집합을 사용하여 임의의 회로를 변환할 수 있도록 표준 등가 라이브러리에 등가 규칙을 추가해야 합니다. 이는 표준 게이트에 대해 사용자 정의 게이트를 기준으로 등가 회로를 정의함으로써 수행할 수 있습니다. 일반적으로 (기준에 표준 2큐비트 게이트가 포함되어 있지 않은 경우)와 및 UGate 와 HGate 같은 흔히 사용되는 단일 큐비트 회전 게이트로 CXGate 변환할 수 있다면, 트랜스파일러가 어떤 회로든 사용자 정의 기준 게이트로 변환하는 데 충분할 것입니다. 하지만, 표준 게이트에서 해당 기저로 정의되는 등가 규칙이 많을수록 임의의 회로를 대상 기저로 변환하는 효율이 높아집니다(비록 항상 그런 것은 아니며, 수익의 증가폭은 점차 줄어들지만).

    HGate예를 들어, 위의 사용자 SYGate 정의에 몇 가지 규칙을 추가한다고 가정하면, 와 를 U2Gate 다음과 같이 정의할 수 있습니다:

    from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary
    from qiskit.circuit.library import HGate
    from qiskit.circuit.library import ZGate
    from qiskit.circuit.library import RZGate
    from qiskit.circuit.library import U2Gate
    
    
    # H => Z SY
    q = qiskit.QuantumRegister(1, "q")
    def_sy_h = qiskit.QuantumCircuit(q)
    def_sy_h.append(ZGate(), [q[0]], [])
    def_sy_h.append(SYGate(), [q[0]], [])
    SessionEquivalenceLibrary.add_equivalence(
        HGate(), def_sy_h)
    
    # u2 => Z SY Z
    phi = qiskit.circuit.Parameter('phi')
    lam = qiskit.circuit.Parameter('lambda')
    q = qiskit.QuantumRegister(1, "q")
    def_sy_u2 = qiskit.QuantumCircuit(q)
    def_sy_u2.append(RZGate(lam), [q[0]], [])
    def_sy_u2.append(SYGate(), [q[0]], [])
    def_sy_u2.append(RZGate(phi), [q[0]], [])
    SessionEquivalenceLibrary.add_equivalence(
        U2Gate(phi, lam), def_sy_u2)

    이 코드는 임포트 시에 실행되도록 설정해야 합니다. 그래야 제공자 패키지가 임포트되는 즉시 코드가 실행됩니다. 이렇게 하면 사용자 정의 게이트를 사용하여 패스를 BasisTranslator 실행할 때마다 동등성 규칙이 정의되도록 보장됩니다.

    또한, 사용 중인 기준에 따라 트랜스파일러의 일부 최적화 단계(예: Optimize1qGatesDecomposition )가 사용자 정의 기준에서는 작동하지 않을 수 있다는 점도 유의해야 합니다. 이 SYGate 예시에서, 는 Optimize1qGatesDecomposition 단일 큐비트 게이트의 연쇄를 SY 기저로 단순화할 수 없을 것입니다. 이는 해당 OneQubitEulerDecomposer 클래스가 SY 기저에서 어떻게 작동해야 하는지 알지 못하기 때문입니다. SYGate이 문제를 해결하려면 해당 SYGate 클래스를 Qiskit에 추가하고, OneQubitEulerDecomposer 로의 분해를 지원하도록 업데이트해야 합니다. 장기적으로 볼 때, 이는 커스텀 베이스 게이트에 있어 더 나은 방향일 것이며, 트랜스파일러에 관련 정의와 지원 기능을 포함시킴으로써 향후에도 Qiskit에서 계속해서 제대로 지원받을 수 있도록 보장할 것입니다.

사용자 정의 트랜스파일러 패스

이 트랜스파일러는 백엔드가 하드웨어별 최적화 및 회로 변환을 용이하게 하기 위해 사용자 정의 트랜스파일러 단계 구현을 제공할 수 있도록 지원합니다. 현재 두 가지 단계가 지원되며, get_translation_stage_plugin() 와 get_scheduling_stage_plugin() 를 통해 백엔드에서 각각 기본 번역 단계 및 기본 스케줄링 단계로 사용할 문자열 플러그인 이름을 지정할 수 있습니다. 클래스 BackendV2 내의 이러한 후크 지점은, 현재 백엔드나Target 인터페이스가 백엔드의 컴파일 요구 사항을 충족하지 못하는 경우에 사용할 수 있습니다. 또한, 더 많은 하드웨어 아키텍처를 보다 심도 있게 기술할 수 있도록 해당 인터페이스를 개선하려는 움직임이 있으므로, 사용 사례를 설명하는 GitHub 이슈를 등록해 주시기 바랍니다.

이러한 후크 지점을 활용하려면 구현체에 BackendV2 해당 메서드를 추가하고, 해당 메서드가 문자열 형식의 플러그인 이름을 반환하도록 설정하기만 하면 됩니다. 예를 들어,

class Mybackend(BackendV2):

    def get_scheduling_stage_plugin(self):
        return "SpecialDD"

    def get_translation_stage_plugin(self):
        return "BasisTranslatorWithCustom1qOptimization"

백엔드 구현의 이 스니펫에는 이제 transpile() 함수는 대상이 Mybackend 로 설정된 경우 기본적으로 스케줄링 단계에는 SpecialDD 플러그인을, 번역 단계에는 BasisTranslatorWithCustom1qOptimization 플러그인을 사용합니다. 사용자가 명시적으로 다른 플러그인 이름을 선택하여 이러한 선택 사항을 재정의할 수 있습니다. 이 인터페이스가 작동하려면 반환된 플러그인 이름에 대해 트랜스파일러 스테이지 플러그인을 구현해야 합니다. 플러그인 구현에 대한 자세한 내용은 qiskit.transpiler.preset_passmanagers.plugin 모듈 설명서를 참조하세요. 일반적으로 백엔드에 컴파일 단계의 일부로 사용자 정의 패스가 필요한 경우 공급자 패키지에는 해당 패스를 사용하는 트랜스파일러 단계 플러그인이 포함됩니다. 그러나 이것은 필수는 아니며 내장 메소드 또는 외부 플러그인에서 유효한 모든 메소드를 사용할 수 있습니다.

이렇게 하면 Mybackend 에서 실행하거나 효율적인 출력을 제공하기 위해 이 두 가지 컴파일 단계가 필요한 경우 트랜스파일러가 수동 사용자 입력 없이도 이러한 사용자 지정 단계를 수행할 수 있습니다.

실시간 변수

트랜스파일러는 실시간으로 입력된 클래식 변수를 자동으로 처리하고( qiskit.circuit.classical)를 자동으로 처리하고 Store 명령어를 내장된 "지시어"로 취급합니다( Barrier. 이를 허용하기 위해 백엔드에서 특별한 처리가 필요하지 않습니다.

백엔드에서 기존 변수 및 저장소를 처리할 수 없는 경우, 문서에 이를 명시하고, 해당 변수나 저장소가 포함된 회로를 즉시 거부하도록 메서드에 run() 확인 로직을 추가하는 것이 좋습니다( Backend.run 메서드 참조). 최상위 수준에서 변수가 있는지 확인할 QuantumCircuit.num_vars 수 있습니다. QuantumCircuit.num_declared_vars제어 흐름 연산을 허용하는 경우, 각 연산의 내부에서 blocks 범위 내 변수를 재귀적으로 검색해야 할 수도 있습니다.

예를 들어, 수동 저장 위치가 있는지 확인하는 기능이나 메모리에 수동 저장하는 기능 등이 있습니다:

from qiskit.circuit import Store, ControlFlowOp, QuantumCircuit

def has_realtime_logic(circuit: QuantumCircuit) -> bool:
    if circuit.num_vars:
        return True
    for instruction in circuit.data:
        if isinstance(instruction.operation, Store):
            return True
        elif isinstance(instruction.operation, ControlFlowOp):
            for block in instruction.operation.blocks:
                if has_realtime_logic(block):
                    return True
    return False

게이트의 각도 제한

Target백엔드에서 타깃 내의 특정 게이트에 대해 허용되는 매개변수 값에 제약 조건이 있는 경우, 이를 에 대한 각도 범위로 모델링할 수 있습니다. 이 add_instruction() 연산자를 추가할 때, 게이트 매개변수의 상한과 하한을 나타내는 튜플 목록을 받는 키워드 인자를 angle_bounds 사용할 수 있습니다.

예를 들어, 위 예제에서 를 PhaseGate 추가하는 대신 다음 코드 조각을 사용할 수 있습니다:

lam = Parameter("λ")
p_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(PhaseGate(lam), p_props, angle_bounds=[(0, math.pi)])

의 PhaseGate 범위를 0과 π\pi (양쪽 끝 포함) 사이로 설정합니다. PhaseGate이는 의 lamTarget 각도 값에 대한 각도 제약 조건을 모델링합니다. 트랜스파일러 패스는 WrapAngles 지정된 각도 범위 밖의 모든 PhaseGate 값을 변환하는 데 사용됩니다. DAGCircuit게이트의 각도 값을 입력으로 받아.를 반환하는 함수를 작성해야 합니다. 예를 들어,

from qiskit.transpiler.passes import WrapAngles

def fold_phase(angles: List[float], qubits: List[int]) -> DAGCircuit:
    angle = angles[0]
    if angle > 0:
        number_of_gates = angle / math.pi
    else:
        number_of_gates = (6.28 - angle) / math.pi
    dag = DAGCircuit()
    dag.add_qubits([Qubit()])
    for _ in range(int(number_of_gates)):
        dag.apply_operation_back(PhaseGate(math.pi), [dag.qubits[0]])
    return dag

WrapAngles.DEFAULT_REGISTRY.add_wrapper("phase", fold_phase)

이 함수는 아웃 오브 바운드 게이트를 타깃의 각도 바운드와 타깃의 다른 제약 조건을 존중하는 게이트로 변환합니다(특별히 좋지는 않지만).

Backend.run 방법

가장 중요한 것은 회로를 장치나 시뮬레이터에 실제로 전송하는 데 사용되는 방법입니다 run() . run 메서드는 회로를 백엔드로 전송하여 실행하게 하고, 객체를 Job 반환하는 역할을 합니다. 백엔드의 유형에 따라, 일반적으로 회로 객체를 해당 백엔드에서 사용하는 API 형식으로 직렬화해야 합니다. 백엔드별 직렬화 요구 사항이 다를 수 있으며(로컬 시뮬레이터의 경우 직렬화가 필요하지 않을 수도 있음), 따라서 백엔드의 run 메서드가 이러한 변환을 처리할 것으로 예상됩니다.

실행 방법의 예는 다음과 같습니다:

def run(self, circuits, **kwargs):
    for kwarg in kwargs:
        if not hasattr(self.options, kwarg):
            warnings.warn(
                "Option %s is not used by this backend" % kwarg,
                UserWarning, stacklevel=2)
    options = {
        'shots': kwargs.get('shots', self.options.shots),
        'memory': kwargs.get('memory', self.options.memory),
    }
    job_json = convert_to_wire_format(circuit, options)
    job_handle = submit_to_backend(job_json)
    return MyJob(self.job_handle, job_json, circuit)

백엔드 옵션

회로가 어떻게 작동하는지를 제어하는 백엔드에는 대개 여러 가지 옵션이 있습니다. 이것의 대표적인 예로는 회로가 몇 번 실행되어야 하는지를 나타내는 ‘수’와 shots 같은 것이 있습니다. 백엔드에 사용할 수 있는 옵션은 객체를 Options 사용하여 정의됩니다. 이 객체는 처음에 백엔드 클래스의 메서드에 _default_options 의해 생성됩니다. 기본 옵션은 백엔드가 지원하는 모든 옵션에 대해 기본값이 설정된 초기화된 Options 객체를 반환합니다. 예를 들어, 백엔드가 오직 메서드만 지원하는 shots 경우, 해당 _default_options 메서드는 다음과 같이 보일 것입니다:

@classmethod
def _default_options(cls):
    return Options(shots=1024)

또한 객체에 Options 유효성 검사기를 설정하여, 백엔드에서 허용하는 기준에 따라 사용자가 입력한 값에 대한 제한 및 유효성 검사를 수행할 수 있습니다. 예를 들어, 위에서 정의한 옵션이 "shots" 1에서 4096 사이의 임의의 값으로 설정될 수 있다면, 백엔드의 옵션 객체에 다음과 같이 유효성 검사기를 설정할 수 있습니다:

self.options.set_validator("shots", (1, 4096))

모든 유효성 검사 옵션 목록을 보려면 문서를 set_validator() 참조하십시오.

작업

이 run 메서드의 반환 값은 객체입니다 JobV1 . 각 프로바이더는 해당 프로바이더의 동작을 정의하는 사용자 정의 작업 하위 클래스를 구현해야 합니다. 백엔드의 실행 방식에 따라 작업은 동기식(sync)과 비동기식(async)의 두 가지 유형으로 나뉩니다. Backend.run()기본적으로 작업은 비동기적인 것으로 간주되며, 이는.을 통해 제출된 회로의 비동기 실행에 대한 핸들을 나타내는 것으로 간주됩니다. 비동기 작업 객체를 통해 사용자는 실행 상태를 조회하고, 실행 중인 작업을 취소하며, 실행이 완료될 때까지 대기할 수 있습니다. 는 result 사용자가 주로 사용하는 메서드로, 실행이 완료될 때까지 대기한 후 작업 결과를 담은 객체를 Result 반환합니다.

일부 백엔드(주로 로컬 시뮬레이터)의 경우, 회로 실행은 동기식 작업이므로 다른 곳에서 실행 중인 작업에 대한 핸들을 반환할 필요가 없습니다. 동기화 작업의 경우, 백엔드의 메서드는 run 객체가 Result 생성될 때까지 차단 상태를 유지하며, 동기화 작업은 해당 내부 Result 객체를 반환할 것으로 예상됩니다.

비동기 API 기반 백엔드를 위한 작업 클래스의 예시는 다음과 같습니다:

from qiskit.providers import JobV1 as Job
from qiskit.providers import JobError
from qiskit.providers import JobTimeoutError
from qiskit.providers.jobstatus import JobStatus
from qiskit.result import Result


class MyJob(Job):
    def __init__(self, backend, job_id, job_json, circuits):
        super().__init__(backend, job_id)
        self._backend = backend
        self.job_json = job_json
        self.circuits = circuits

    def _wait_for_result(self, timeout=None, wait=5):
        start_time = time.time()
        result = None
        while True:
            elapsed = time.time() - start_time
            if timeout and elapsed >= timeout:
                raise JobTimeoutError('Timed out waiting for result')
            result = get_job_status(self._job_id)
            if result['status'] == 'complete':
                break
            if result['status'] == 'error':
                raise JobError('Job error')
            time.sleep(wait)
        return result

    def result(self, timeout=None, wait=5):
        result = self._wait_for_result(timeout, wait)
        results = [{'success': True, 'shots': len(result['counts']),
                    'data': result['counts']}]
        return Result.from_dict({
            'results': results,
            'backend_name': self._backend.configuration().backend_name,
            'backend_version': self._backend.configuration().backend_version,
            'job_id': self._job_id,
            'success': True,
        })

    def status(self):
        result = get_job_status(self._job_id)
        if result['status'] == 'running':
            status = JobStatus.RUNNING
        elif result['status'] == 'complete':
            status = JobStatus.DONE
        else:
            status = JobStatus.ERROR
        return status

    def submit(self):
        raise NotImplementedError

동기화 작업을 위해:

class MySyncJob(Job):
    _async = False

    def __init__(self, backend, job_id, result):
        super().__init__(backend, job_id)
        self._result = result

    def submit(self):
        return

    def result(self):
        return self._result

    def status(self):
        return JobStatus.DONE

기본

공급자 인터페이스에 직접적으로 포함되지는 않지만 qiskit.primitives 모듈은 공급자와 긴밀하게 연결되어 있습니다. 특히 BaseSampler 및 BaseEstimator 과 같은 기본 인터페이스는 공급자 구현이 공급자의 백엔드에 최적화된 사용자 정의 구현을 제공할 수 있도록 설계되었습니다. 여기에는 회로 변환, 추가 사전 및 사후 처리, 배치, 캐싱, 오류 완화 등과 같은 사용자 지정이 포함될 수 있습니다. 모듈의 개념은 qiskit.primitives 모듈의 개념은 원시 객체가 상위 수준의 추상화이므로 이를 명시적으로 활성화하여 최상의 결과를 효율적으로 얻는 메커니즘을 추상화한 처리된 상위 수준의 출력(예: 확률 분포 및 기대값)을 생성하고, 이러한 출력을 사용하는 상위 수준의 애플리케이션에 집중할 수 있도록 하는 것입니다.

예를 들어, 백엔드에서 mthree 측정 완화를 활용하여 결과의 품질을 개선하는 데 적합한 경우 내부적으로 M3Mitigation 클래스를 활용하여 회로를 실행하고 결과의 mthree에서 직접 준확률을 반환하는 공급자별 Sampler 구현을 구현할 수 있습니다. 이렇게 하면 알고리즘이 백엔드에서 직접 완화를 적용하여 최상의 결과를 얻을 수 있습니다. 사용자 지정 구현을 작성하는 방법은 qiskit.primitives 에서 사용자 정의 구현을 작성하는 방법에 대한 문서를 참조하세요. 또한 내장된 구현: Sampler, Estimator, BackendSampler, BackendEstimator 도 구현 방법에 대한 참조/모델로 사용할 수 있습니다.

이 페이지가 도움이 되었습니까?
GitHub에서 버그, 오타를 보고하거나 컨텐츠를 요청하십시오.