주 콘텐츠로 건너뛰기

Qiskit SDK primitives를 사용한 정밀 시뮬레이션

패키지 버전

이 페이지의 코드는 다음 요구 사항을 사용하여 개발되었습니다. 이 버전 이상을 사용하는 것을 권장합니다.

qiskit[all]~=2.3.0

Qiskit SDK의 참조 primitives는 로컬 statevector 시뮬레이션을 수행합니다. 이러한 시뮬레이션은 디바이스 노이즈 모델링을 지원하지 않지만, 더 고급 시뮬레이션 기법(Qiskit Aer 사용)이나 실제 디바이스 실행(Qiskit Runtime primitives)으로 나아가기 전에 알고리즘을 빠르게 프로토타이핑하는 데 유용합니다.

Estimator primitive는 Circuit의 기댓값을 계산할 수 있으며, Sampler primitive는 Circuit의 출력 분포에서 샘플링할 수 있습니다.

다음 섹션에서는 참조 primitives를 사용하여 워크플로우를 로컬에서 실행하는 방법을 보여줍니다.

참조 Estimator 사용

로컬 statevector 시뮬레이터에서 실행되는 qiskit.primitivesEstimatorV2 참조 구현체는 StatevectorEstimator 클래스입니다. Circuit, 관측량(observables), 파라미터를 입력으로 받아 로컬에서 계산된 기댓값을 반환합니다.

다음 코드는 이어지는 예제에서 사용할 입력을 준비합니다. 관측량의 예상 입력 타입은 qiskit.quantum_info.SparsePauliOp입니다. 예제의 Circuit은 파라미터화되어 있지만, 파라미터화되지 않은 Circuit에서도 Estimator를 실행할 수 있습니다.

참고

Estimator에 전달되는 모든 Circuit에는 측정(measurements)이 포함되어서는 안 됩니다.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")

Output of the previous code cell

from qiskit.quantum_info import SparsePauliOp
import numpy as np

# observable(s) whose expected values you want to compute

observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])

# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
ISA Circuit 및 관측량으로 Transpile하기

Qiskit Runtime primitives 워크플로우는 Circuit과 관측량을 QPU가 지원하는 명령어만 사용하도록 변환해야 합니다(이를 명령어 집합 아키텍처(ISA) Circuit 및 관측량이라고 합니다). 참조 primitives는 로컬 statevector 시뮬레이션에 의존하므로 추상 명령어도 허용하지만, Circuit을 Transpile하면 Circuit 최적화 측면에서 여전히 이점이 있을 수 있습니다.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

Estimator 초기화

qiskit.primitives.StatevectorEstimator를 인스턴스화합니다.

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

실행 및 결과 가져오기

이 예제는 하나의 Circuit(타입: QuantumCircuit)과 하나의 관측량만 사용합니다.

StatevectorEstimator.run 메서드를 호출하여 추정을 실행합니다. 이 메서드는 PrimitiveJob 객체의 인스턴스를 반환합니다. qiskit.primitives.PrimitiveJob.result 메서드를 사용하여 작업에서 결과(qiskit.primitives.PrimitiveResult 객체)를 가져올 수 있습니다.

job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>

결과에서 기댓값 가져오기

primitives 결과는 PubResult 객체의 배열을 출력합니다. 배열의 각 항목은 PUB 내의 모든 Circuit-관측량 조합에 해당하는 평가 배열을 데이터로 포함하는 PubResult 객체입니다.

첫 번째(이 경우 유일한) Circuit 평가에 대한 기댓값과 메타데이터를 가져오려면 PUB 0의 평가 data에 접근해야 합니다.

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
> Expectation value: [4.         3.73205081 2.        ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}

Estimator 실행 옵션 설정

기본적으로 참조 Estimator는 quantum_info.Statevector 클래스를 기반으로 정밀한 statevector 계산을 수행합니다. 그러나 샘플링 오버헤드(일명 "샷 노이즈")의 효과를 도입하도록 수정할 수 있습니다.

Estimator는 primitive 구현이 기댓값 추정에서 목표로 해야 하는 오차 범위를 나타내는 precision 인수를 허용합니다. 이는 샘플링 오버헤드이며 .run() 메서드에서만 정의됩니다. 이를 통해 PUB 수준까지 세밀하게 옵션을 조정할 수 있습니다.

# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
[(circuit, observable, parameter_values)], precision=0.05
)

전체 예제는 Primitives 예제 페이지를 참조하세요.

참조 Sampler 사용

qiskit.primitivesSamplerV2 참조 구현체는 StatevectorSampler 클래스입니다. Circuit과 파라미터를 입력으로 받아 출력 확률 분포에서 샘플링한 결과를 출력 상태의 준확률 분포로 반환합니다.

다음 코드는 이어지는 예제에서 사용할 입력을 준비합니다. 이 예제는 파라미터화된 단일 Circuit을 실행하지만, 파라미터화되지 않은 Circuit에서도 Sampler를 실행할 수 있습니다.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")

Output of the previous code cell

참고

Sampler에 전달되는 모든 양자 Circuit에는 측정이 포함되어야 합니다.

ISA Circuit 및 관측량으로 Transpile하기

Qiskit Runtime primitives 워크플로우는 Circuit을 QPU가 지원하는 명령어만 사용하도록 변환해야 합니다(ISA Circuit이라고 합니다). 참조 primitives는 로컬 statevector 시뮬레이션에 의존하므로 추상 명령어도 허용하지만, Circuit을 Transpile하면 Circuit 최적화 측면에서 여전히 이점이 있을 수 있습니다.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)

SamplerV2 초기화

qiskit.primitives.StatevectorSampler를 인스턴스화합니다.

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

실행 및 결과 가져오기

# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

Primitives는 여러 PUB를 입력으로 허용하며, 각 PUB는 자체 결과를 갖습니다. 따라서 다양한 파라미터/관측량 조합으로 여러 Circuit을 실행하고 PUB 결과를 가져올 수 있습니다.

from qiskit.transpiler import generate_preset_pass_manager

# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()

# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

확률 분포 또는 측정 결과 가져오기

측정 결과 샘플은 비트스트링 또는 카운트로 반환됩니다. 비트스트링은 측정된 순서대로 측정 결과를 보여줍니다. Sampler 결과 객체는 동적 Circuit과의 호환성을 위해 입력 Circuit의 고전 레지스터 이름을 기준으로 데이터를 구성합니다.

참고

고전 레지스터의 기본 이름은 "meas"입니다. 이 이름은 나중에 측정 비트스트링에 접근하는 데 사용됩니다.

# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()
┌───┐      ░ ┌─┐   
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'11': 515, '00': 509}

실행 옵션 변경

기본적으로 참조 Sampler는 quantum_info.Statevector 클래스를 기반으로 정밀한 statevector 계산을 수행합니다. 그러나 샘플링 오버헤드(일명 "샷 노이즈")의 효과를 도입하도록 수정할 수 있습니다. 이 오버헤드를 관리하기 위해 Sampler 인터페이스는 PUB 수준에서 정의할 수 있는 shots 인수를 허용합니다.

이 예제에서는 두 개의 Circuit이 정의되어 있다고 가정합니다.

# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_circuit2], shots=128)
# Sample two circuits at different amounts of shots. The "None"s are necessary
# as placeholders
# for the lack of parameter values in this example.
sampler.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])
<qiskit.primitives.primitive_job.PrimitiveJob at 0x7fa430e39dd0>

전체 예제는 Primitives 예제 페이지를 참조하세요.

다음 단계

권장 사항