Skip to main content
IBM Quantum Platform

깊이 감소를 위한 회로 절단

사용량 추정치: Eagle 프로세서에서 8분(참고: 이는 추정치일 뿐입니다. 런타임은 다를 수 있습니다.)


배경

이 튜토리얼에서는 양자 회로에서 게이트를 절단하여 회로 깊이를 줄이기 위한 Qiskit pattern 을 구축하는 방법을 보여줍니다. 회로 차단에 대한 자세한 내용은 회로 차단 키스킷 애드온 문서를 참조하세요.


요구사항

이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:

  • Qiskit SDK v2.0 또는 이후 버전, 시각화 지원 기능 포함
  • Qiskit Runtime v0.22 또는 이후 (pip install qiskit-ibm-runtime)
  • 회로 절단 Qiskit 애드온 v0.9.0 또는 이후 버전 (pip install qiskit-addon-cutting)

설정

import numpy as np

from qiskit.circuit.library import EfficientSU2
from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_addon_cutting import (
    cut_gates,
    generate_cutting_experiments,
    reconstruct_expectation_values,
)

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

1단계: 고전적 입력을 양자 문제에 매핑하기

문서에 설명된 네 단계를 사용하여 키스킷 패턴을 구현하겠습니다. 이 경우, 특정 깊이의 회로에서 기대값을 시뮬레이션하여 스왑 게이트를 생성하고 더 얕은 회로에서 하위 실험을 실행합니다. 게이트 절단은 2단계(멀리 떨어진 게이트를 분해하여 양자 실행을 위한 회로 최적화)와 4단계(원래 회로에서 기대값을 재구성하는 후처리)와 관련이 있습니다. 첫 번째 단계에서는 키스킷 회로 라이브러리에서 회로를 생성하고 몇 가지 옵저버를 정의합니다.

  • 입력: 회로를 정의하는 기존 파라미터
  • 출력: 추상 회로 및 관측 가능
circuit = EfficientSU2(num_qubits=4, entanglement="circular").decompose()
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
observables = PauliList(["ZZII", "IZZI", "IIZZ", "XIXI", "ZIZZ", "IXIX"])
circuit.draw("mpl", scale=0.8, style="iqp")

Output:

Output of the previous code cell

2단계: 양자 하드웨어 실행을 위한 문제 최적화

  • 입력: 추상 회로 및 관측값
  • 출력: 목표 회로 및 원거리 게이트를 절단하여 투명 회로 깊이를 줄이기 위해 생성된 관측 가능 항목

큐비트 3과 0 사이의 게이트를 실행하기 위해 두 번의 스왑이 필요한 초기 레이아웃을 선택하고 큐비트를 초기 위치로 되돌리기 위해 또 다른 두 번의 스왑이 필요한 초기 레이아웃을 선택합니다. 저희는 사전 설정된 패스 관리자로 사용할 수 있는 최고 수준의 최적화인 optimization_level=3 을 선택합니다.

service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, min_num_qubits=circuit.num_qubits, simulator=False
)

pm = generate_preset_pass_manager(
    optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend
)
transpiled_qc = pm.run(circuit)
스왑해야 하는 큐비트를 보여주는 커플링 맵
print(f"Transpiled circuit depth: {transpiled_qc.depth()}")
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, style="iqp", fold=-1)

Output:

Transpiled circuit depth: 103
Output of the previous code cell

먼 게이트를 찾아 잘라내라: 우리는 먼 게이트(비국소 큐비트인 0과 3을 연결하는 게이트)를 해당 인덱스를 지정하여 객체로 TwoQubitQPDGate 대체할 것이다. cut_gates 지정된 인덱스의 게이트를 객체로 TwoQubitQPDGate 대체하고, 게이트 분해마다 하나씩 총 QPDBasis 인스턴스 목록을 반환합니다. 해당 QPDBasis 객체는 커트 게이트를 단일 큐비트 연산으로 분해하는 방법에 대한 정보를 포함합니다.

# Find the indices of the distant gates
cut_indices = [
    i
    for i, instruction in enumerate(circuit.data)
    if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}
]

# Decompose distant CNOTs into TwoQubitQPDGate instances
qpd_circuit, bases = cut_gates(circuit, cut_indices)

qpd_circuit.draw("mpl", scale=0.8)

Output:

Output of the previous code cell

백엔드에서 실행할 하위 실험 생성 : generate_cutting_experimentsTwoQubitQPDGate 인스턴스와 관측값을 포함하는 회로를 PauliList 로 받아들입니다.

전체 크기 회로의 기대값을 시뮬레이션하기 위해 분해된 게이트의 공동 준확률 분포에서 많은 하위 실험을 생성한 다음 하나 이상의 백엔드에서 실행합니다. 분포에서 가져온 샘플 수는 num_samples 에 의해 제어되며, 각 고유 샘플에 대해 하나의 결합 계수가 주어집니다. 계수 계산 방법에 대한 자세한 내용은 설명 자료를 참조하세요.

# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
    circuits=qpd_circuit, observables=observables, num_samples=np.inf
)

비교를 위해 멀리 떨어진 게이트를 절단하면 QPD 하위 실험이 더 얕아지는 것을 볼 수 있습니다 : 다음은 QPD 회로에서 임의로 선택한 하위 실험의 예입니다. 깊이가 절반 이상 줄었습니다. 더 깊은 회로의 기대값을 재구성하기 위해서는 이러한 확률적 하위 실험을 많이 생성하고 평가해야 합니다.

# Transpile the decomposed circuit to the same layout
transpiled_qpd_circuit = pm.run(subexperiments[100])

print(f"Original circuit depth after transpile: {transpiled_qc.depth()}")
print(
    f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}"
)
transpiled_qpd_circuit.draw(
    "mpl", scale=0.6, style="iqp", idle_wires=False, fold=-1
)

Output:

Original circuit depth after transpile: 103
QPD subexperiment depth after transpile: 46
Output of the previous code cell

반면에 절단은 추가 샘플링이 필요합니다. 여기서는 3개의 CNOT 게이트를 절단하여 샘플링 오버헤드가 939^3 로 발생했습니다. 회로 절단으로 인해 발생하는 샘플링 오버헤드에 대한 자세한 내용은 회로 편직 툴박스 문서를 참조하세요.

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")

Output:

Sampling overhead: 729.0

3단계: Qiskit primitives 명령어로 실행합니다

Sampler 프리미티브를 사용하여 대상 회로(“하위 실험”)를 실행합니다.

  • 입력: 대상 회로
  • 출력: 준확률 분포
# Transpile the subexperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pm.run(subexperiments)

# Set up the IBM Quantum Sampler primitive.  For a fake backend, this will use a local simulator.
sampler = SamplerV2(backend)

# Submit the subexperiments
job = sampler.run(isa_subexperiments)
# Retrieve the results
results = job.result()
print(job.job_id())

Output:

czypg1r6rr3g008mgp6g

4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환

하위 실험 결과, 하위 관측 변수 및 샘플링 계수를 사용하여 원래 회로의 기대값을 재구성합니다.

입력: 준확률 분포 출력: 재구성된 기대값

reconstructed_expvals = reconstruct_expectation_values(
    results,
    coefficients,
    observables,
)
# Reconstruct final expectation value
final_expval = np.dot(reconstructed_expvals, [1] * len(observables))
print("Final reconstructed expectation value")
print(final_expval)

Output:

Final reconstructed expectation value
1.0751342773437473
ideal_expvals = [
    Statevector(circuit).expectation_value(SparsePauliOp(observable))
    for observable in observables
]
print("Ideal expectation value")
print(np.dot(ideal_expvals, [1] * len(observables)).real)

Output:

Ideal expectation value
1.2283177520039992

튜토리얼 설문조사

이 튜토리얼에 대한 피드백을 제공하려면 간단한 설문조사에 참여해 주세요. 여러분의 인사이트는 콘텐츠 제공과 사용자 경험을 개선하는 데 도움이 됩니다.

설문조사 링크

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