Skip to main content
IBM Quantum Platform

QESEM 함수를 사용하여 기울어진 필드 이징 모델( 2D ) 시뮬레이션 수행

Note

Qiskit Functions 는 IBM Quantum® Premium Plan, Flex 요금제 및 On-Prem ( IBM Quantum Platform API를 통해) 요금제 사용자에게만 제공되는 실험적 기능입니다. 프리뷰 릴리스 상태이며 변경될 수 있습니다.

사용 예상 시간: Heron r2 프로세서에서 20분. (참고: 이는 추정치일 뿐입니다. 런타임은 다를 수 있습니다.)


배경

이 튜토리얼에서는 Qedma의 키스킷 함수인 QESEM을 사용하여 비클리포드 각을 갖는 표준 양자 스핀 모델인 2D 틸티드 필드 아이싱(TFI) 모델의 동역학을 시뮬레이션하는 방법을 보여드립니다:

H=Ji,jZiZj+gxiXi+gziZi,H = J \sum_{\langle i,j \rangle} Z_i Z_j + g_x \sum_i X_i + g_z \sum_i Z_i ,

여기서 i,j\langle i,j \rangle 는 격자에서 가장 가까운 이웃을 나타냅니다. 다체 양자 시스템의 시간 진화를 시뮬레이션하는 것은 기존 컴퓨터로는 계산적으로 어려운 작업입니다. 반면 양자 컴퓨터는 이 작업을 효율적으로 수행할 수 있도록 설계되었습니다. 특히 TFI 모델은 풍부한 물리적 동작과 하드웨어 친화적인 구현으로 인해 양자 하드웨어에서 널리 사용되는 벤치마크가 되었습니다.

연속 시간 역학을 시뮬레이션하는 대신 밀접하게 관련된 킥 아이싱 모델을 채택했습니다. 동역학은 주기적 양자 회로로 정확히 표현할 수 있는데, 각 진화 단계는 세 층의 분수 2 큐비트 게이트 RZZ(αZZ)R_{ZZ} (\alpha_{ZZ}) 와 단일 큐비트 게이트 RX(αX)R_X (\alpha_X)RZ(αZ)R_Z (\alpha_Z) 층으로 구성됩니다.

기존 시뮬레이션과 오류 완화 모두에 어려운 일반 각도를 사용할 것입니다. 특히 αZZ=1.0\alpha_{ZZ} = 1.0, αX=0.53\alpha_X = 0.53, αZ=0.1\alpha_Z = 0.1 를 선택하여 통합 가능한 지점에서 멀리 떨어진 곳에 모델을 배치했습니다.

이 튜토리얼에서는 다음을 수행합니다:

  • QESEM의 분석 및 경험적 시간 추정 기능을 사용하여 전체 오류 완화를 위한 예상 QPU 런타임을 추정합니다.
  • 하드웨어에서 영감을 얻은 큐비트 레이아웃과 게이트 레이어를 사용하여 2D 틸트 필드 아이싱 모델 회로를 구성하고 시뮬레이션합니다.
  • 실험을 위해 디바이스 큐비트 연결과 선택한 하위 그래프를 시각화합니다.
  • 연산자 역전파(OBP) 를 사용하여 회로 깊이를 줄이는 방법을 시연하십시오. 이 기술은 회로 끝에서 연산을 줄이는 대신 연산자 측정을 더 많이 수행하는 대가를 치릅니다.
  • QESEM을 사용하여 여러 관측값에 대해 동시에 편향되지 않은 오류 완화(EM)를 수행하고 이상적인 결과, 노이즈가 있는 결과, 완화한 결과를 비교합니다.
  • 다양한 회로 깊이에서 오류 완화가 자화에 미치는 영향을 분석하고 그래프로 표시합니다.

참고: OBP는 일반적으로 출퇴근하지 않을 가능성이 있는 관측값 세트를 반환합니다. 대상 관측값에 비통근 기간이 포함되어 있는 경우 QESEM은 자동으로 측정 기준을 최적화합니다. 여러 휴리스틱 알고리즘을 사용하여 후보 측정 기준 집합을 생성하고 고유한 기준의 수를 최소화하는 집합을 선택합니다. 즉, QESEM은 호환 가능한 관측값을 공통 기반으로 그룹화하여 필요한 총 측정 구성 수를 줄여 효율성을 개선합니다.


QESEM에 관하여

QESEM은 효율적이고 편향되지 않은 준확률적 오류 완화를 구현하는 신뢰할 수 있고 정확도가 높은 특성화 기반 소프트웨어입니다. 일반적인 양자 회로의 오류를 완화하도록 설계되었으며 애플리케이션에 구애받지 않습니다. 이 솔루션은 다양한 하드웨어 플랫폼에서 검증되었으며, 여기에는 IBM® Eagle 및 Heron 디바이스에서의 유틸리티 규모 실험이 포함됩니다. QESEM 워크플로 단계는 다음과 같습니다:

  1. 디바이스 특성화 - 게이트 충실도를 매핑하고 일관된 오류를 식별하여 실시간 보정 데이터를 제공합니다. 이 단계에서는 가능한 가장 충실도가 높은 작업을 활용하여 완화를 진행합니다.
  2. 노이즈 인식 변환 - 대체 큐비트 매핑, 연산 세트 및 측정 기반을 생성 및 평가하여 예상 QPU 런타임을 최소화하는 변형을 선택하고, 병렬화 옵션을 통해 데이터 수집을 가속화할 수 있습니다.
  3. 오류 억제 - 기본 게이트를 재정의하고, 폴리 트월링을 적용하며, 펄스 레벨 제어를 최적화하여(지원되는 플랫폼에서) 충실도를 향상시킵니다.
  4. 회로 특성화 - 맞춤형 로컬 오류 모델을 구축하고 이를 QPU 측정에 적용하여 잔여 노이즈를 정량화합니다.
  5. 오류 완화 - 다중 유형의 준확률적 분해를 구성하고, 이를 적응형 프로세스로 샘플링하여 완화 QPU 시간과 하드웨어 변동에 대한 민감도를 최소화함으로써 대규모 회로 볼륨에서 높은 정확도를 달성합니다.

QESEM에 대한 자세한 내용과, 원시 헤비-헥스(heavy-hex) 기하학적 구조의 103큐비트 고연결성 부분 그래프를 대상으로 수행된 이 모델의 실용 규모 실험에 대해서는 ibm_marrakesh, “실용 규모 양자 회로를 위한 신뢰할 수 있는 고정밀 오류 완화(Reliable high-accuracy error mitigation for utility-scale quantum circuits )” 논문을 참조하십시오.

QESEM 워크플로.

요구사항

노트북을 실행하기 전에 다음 Python 패키지를 설치하세요:

  • Qiskit SDK v2.0.0 이상 (pip install qiskit)
  • Qiskit Runtime v0.40.0 이상 (pip install qiskit-ibm-runtime)
  • Qiskit Functions Catalog v0.8.0 이상 ( pip install qiskit-ibm-catalog )
  • 운영자 역전파 키스킷 애드온 v0.3.0 이상 ( pip install qiskit-addon-obp )
  • 키스킷 유틸리티 애드온 v0.1.1 이상 ( pip install qiskit-addon-utils )
  • 키스킷 에어 시뮬레이터 v0.17.1 이상 ( pip install qiskit-aer )
  • Matplotlib v3.10.3 이상 ( pip install matplotlib )

설정

먼저 관련 라이브러리를 가져옵니다:

%matplotlib inline

from typing import Sequence

import matplotlib.pyplot as plt
import numpy as np

import qiskit
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_aer import AerSimulator
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
from qiskit.visualization import (
    plot_gate_map,
)

다음으로, IBM Quantum Platform 대시보드에서 API 키를 사용하여 인증하십시오. 그런 다음 다음과 같이 Qiskit 함수를 선택합니다. (보안상의 이유로, 신뢰할 수 있는 컴퓨터를 사용하는 경우 계정 인증 정보를 로컬 환경에 저장해 두는 것이 가장 좋습니다. 그래야 인증할 때마다 API 키를 입력할 필요가 없습니다.)

# Paste here your instance and token strings

instance = "YOUR_INSTANCE"
token = "YOUR_TOKEN"
channel = "ibm_quantum_platform"

catalog = QiskitFunctionsCatalog(
    channel=channel, token=token, instance=instance
)
qesem_function = catalog.load("qedma/qesem")

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

먼저 트로터 회로를 생성하는 함수를 정의합니다:

def trotter_circuit_from_layers(
    steps: int,
    theta_x: float,
    theta_z: float,
    theta_zz: float,
    layers: Sequence[Sequence[tuple[int, int]]],
    init_state: str | None = None,
) -> qiskit.QuantumCircuit:
    """
    Generates an ising trotter circuit
    :param steps: trotter steps
    :param theta_x: RX angle
    :param theta_z: RZ angle
    :param theta_zz: RZZ angle
    :param layers: list of layers (can be list of layers in device)
    :param init_state: Initial state to prepare.
     If None, will not prepare any state. If "+", will
     add Hadamard gates to all qubits.
    :return: QuantumCircuit
    """
    qubits = sorted({i for layer in layers for edge in layer for i in edge})
    circ = qiskit.QuantumCircuit(max(qubits) + 1)

    if init_state == "+":
        print("init_state = +")
        for q in qubits:
            circ.h(q)

    for _ in range(steps):
        for q in qubits:
            circ.rx(theta_x, q)
            circ.rz(theta_z, q)

        for layer in layers:
            for edge in layer:
                circ.rzz(theta_zz, *edge)
        circ.barrier(qubits)

    return circ

다음으로 AerSimulator 을 사용하여 이상적인 기대값을 계산하는 함수를 만듭니다.

단, 대규모 회로(30 큐비트 이상)의 경우, 신념 전파(BP) PEPS 시뮬레이션에서 미리 계산된 값을 사용하는 것을 권장합니다. 이 코드에는 텐서 네트워크 Python 용 quimb 패키지를 사용하여, 본 논문에서 소개된 PEPS 텐서 네트워크를 진화시키기 위한 BP 접근법(이하 PEPS-BP라고 칭함)을 기반으로 35 큐비트에 대한 사전 계산된 값이 예시로 포함되어 있습니다.

def calculate_ideal_evs(circ, obs, num_qubits, step):
    # Predefined results for large circuits - calculated using
    # bppeps for 3, 5, 7, 9 trotter steps
    predefined_35 = [
        0.79537,
        0.78653,
        0.79699,
    ]

    if num_qubits == 35:
        print(
            "Using precalculated ideal values for large circuits calculated "
            "with belief propagation PEPS. Currently only for 35 qubits."
        )
        return predefined_35[step]

    else:
        simulator = AerSimulator()

        # Use Estimator primitive to get expectation value
        estimator = Estimator(simulator)
        sim_result = estimator.run([(circ, [obs])], precision=0.0001).result()

        # Extracting the result
        ideal_values = sim_result[0].data.evs[0]
        return ideal_values

하드웨어 기반 RZZR_{ZZ} 레이어 매핑을 Heron 장치에서 가져와서 시뮬레이션하려는 큐비트 수에 따라 레이어를 잘라냅니다. 2D 구조를 유지하는 10, 21, 28, 35 큐비트에 대한 하위 그래프를 정의합니다(원하는 하위 그래프로 자유롭게 변경 가능):

LAYERS_HERON_R2 = [  # the full set of hardware layers for Heron r2
    [
        (2, 3),
        (6, 7),
        (10, 11),
        (14, 15),
        (20, 21),
        (16, 23),
        (24, 25),
        (17, 27),
        (28, 29),
        (18, 31),
        (32, 33),
        (19, 35),
        (36, 41),
        (42, 43),
        (37, 45),
        (46, 47),
        (38, 49),
        (50, 51),
        (39, 53),
        (60, 61),
        (56, 63),
        (64, 65),
        (57, 67),
        (68, 69),
        (58, 71),
        (72, 73),
        (59, 75),
        (76, 81),
        (82, 83),
        (77, 85),
        (86, 87),
        (78, 89),
        (90, 91),
        (79, 93),
        (94, 95),
        (100, 101),
        (96, 103),
        (104, 105),
        (97, 107),
        (108, 109),
        (98, 111),
        (112, 113),
        (99, 115),
        (116, 121),
        (122, 123),
        (117, 125),
        (126, 127),
        (118, 129),
        (130, 131),
        (119, 133),
        (134, 135),
        (140, 141),
        (136, 143),
        (144, 145),
        (137, 147),
        (148, 149),
        (138, 151),
        (152, 153),
        (139, 155),
    ],
    [
        (1, 2),
        (3, 4),
        (5, 6),
        (7, 8),
        (9, 10),
        (11, 12),
        (13, 14),
        (21, 22),
        (23, 24),
        (25, 26),
        (27, 28),
        (29, 30),
        (31, 32),
        (33, 34),
        (40, 41),
        (43, 44),
        (45, 46),
        (47, 48),
        (49, 50),
        (51, 52),
        (53, 54),
        (55, 59),
        (61, 62),
        (63, 64),
        (65, 66),
        (67, 68),
        (69, 70),
        (71, 72),
        (73, 74),
        (80, 81),
        (83, 84),
        (85, 86),
        (87, 88),
        (89, 90),
        (91, 92),
        (93, 94),
        (95, 99),
        (101, 102),
        (103, 104),
        (105, 106),
        (107, 108),
        (109, 110),
        (111, 112),
        (113, 114),
        (120, 121),
        (123, 124),
        (125, 126),
        (127, 128),
        (129, 130),
        (131, 132),
        (133, 134),
        (135, 139),
        (141, 142),
        (143, 144),
        (145, 146),
        (147, 148),
        (149, 150),
        (151, 152),
        (153, 154),
    ],
    [
        (3, 16),
        (7, 17),
        (11, 18),
        (22, 23),
        (26, 27),
        (30, 31),
        (34, 35),
        (21, 36),
        (25, 37),
        (29, 38),
        (33, 39),
        (41, 42),
        (44, 45),
        (48, 49),
        (52, 53),
        (43, 56),
        (47, 57),
        (51, 58),
        (62, 63),
        (66, 67),
        (70, 71),
        (74, 75),
        (61, 76),
        (65, 77),
        (69, 78),
        (73, 79),
        (81, 82),
        (84, 85),
        (88, 89),
        (92, 93),
        (83, 96),
        (87, 97),
        (91, 98),
        (102, 103),
        (106, 107),
        (110, 111),
        (114, 115),
        (101, 116),
        (105, 117),
        (109, 118),
        (113, 119),
        (121, 122),
        (124, 125),
        (128, 129),
        (132, 133),
        (123, 136),
        (127, 137),
        (131, 138),
        (142, 143),
        (146, 147),
        (150, 151),
        (154, 155),
        (0, 1),
        (4, 5),
        (8, 9),
        (12, 13),
        (54, 55),
        (15, 19),
    ],
]

subgraphs = {  # the subgraphs for the different qubit counts such that it's 2D
    10: list(range(22, 29)) + [16, 17, 37],
    21: list(range(3, 12)) + list(range(23, 32)) + [16, 17, 18],
    28: list(range(3, 12))
    + list(range(23, 32))
    + list(range(45, 50))
    + [16, 17, 18, 37, 38],
    35: list(range(3, 12))
    + list(range(21, 32))
    + list(range(41, 50))
    + [16, 17, 18, 36, 37, 38],
    42: list(range(3, 12))
    + list(range(21, 32))
    + list(range(41, 50))
    + list(range(63, 68))
    + [16, 17, 18, 36, 37, 38, 56, 57],
}

n_qubits = 35  # 21, 28, 35, 42
layers = [
    [
        edge
        for edge in layer
        if edge[0] in subgraphs[n_qubits] and edge[1] in subgraphs[n_qubits]
    ]
    for layer in LAYERS_HERON_R2
]

print(layers)

Output:

[[(6, 7), (10, 11), (16, 23), (24, 25), (17, 27), (28, 29), (18, 31), (36, 41), (42, 43), (37, 45), (46, 47), (38, 49)], [(3, 4), (5, 6), (7, 8), (9, 10), (21, 22), (23, 24), (25, 26), (27, 28), (29, 30), (43, 44), (45, 46), (47, 48)], [(3, 16), (7, 17), (11, 18), (22, 23), (26, 27), (30, 31), (21, 36), (25, 37), (29, 38), (41, 42), (44, 45), (48, 49), (4, 5), (8, 9)]]

이제 선택한 하위 그래프에 대한 Heron 디바이스의 큐비트 레이아웃을 시각화합니다:

catalog = QiskitFunctionsCatalog(
    channel=channel,
    token=token,
    instance=instance,
)
backend = catalog.backend("ibm_fez")  # or any available device

selected_qubits = subgraphs[n_qubits]
num_qubits = backend.configuration().num_qubits
qubit_color = [
    "#ff7f0e" if i in selected_qubits else "#d3d3d3"
    for i in range(num_qubits)
]

plot_gate_map(
    backend=backend,
    figsize=(15, 10),
    qubit_color=qubit_color,
)
plt.show()

Output:

Output of the previous code cell

선택한 큐비트 레이아웃의 연결이 반드시 선형적일 필요는 없으며, 선택한 큐비트 수에 따라 Heron 디바이스의 넓은 영역을 커버할 수 있습니다.

이제 트로터 회로와 선택한 큐비트 수와 파라미터에 대해 관찰 가능한 평균 자화를 생성합니다:

# Chosen parameters:
theta_x = 0.53
theta_z = 0.1
theta_zz = 1.0
steps = 9

circ = trotter_circuit_from_layers(steps, theta_x, theta_z, theta_zz, layers)
print(
    f"Circuit 2q layers: "
    f"{circ.depth(filter_function=lambda instr: len(instr.qubits) == 2)}"
)
print("\nCircuit structure:")

circ.draw("mpl", scale=0.8, fold=-1, idle_wires=False)
plt.show()

observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
    [("Z", [q], 1 / n_qubits) for q in subgraphs[n_qubits]],
    np.max(subgraphs[n_qubits]) + 1,
)  # Average magnetization observable

print(observable)
obs_list = [observable]

Output:

Circuit 2q layers: 27

Circuit structure:
Output of the previous code cell
SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
              coeffs=[0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j])

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

OBP 적용 여부에 따른 QPU 시간 추정

사용자는 일반적으로 실험에 필요한 QPU 시간을 알고 싶어 합니다. 그러나 이것은 기존 컴퓨터에서는 어려운 문제로 간주됩니다.

QESEM은 사용자에게 실험의 실현 가능성을 알려주는 두 가지 시간 추정 모드를 제공합니다:

  1. 분석 시간 추정 - 매우 대략적인 추정치를 제공하며 QPU 시간이 필요하지 않습니다. 트랜스파일레이션 패스가 잠재적으로 QPU 시간을 줄일 수 있는지 테스트하는 데 사용할 수 있습니다.
  2. 경험적 시간 추정(여기에서 시연) - 꽤 괜찮은 추정치를 제공하며 몇 분의 QPU 시간을 사용합니다.

두 경우 모두 QESEM은 모든 관측값에 대해 필요한 정밀도에 도달하기 위한 시간 추정치를 출력합니다.

run_on_real_hardware = True

precision = 0.05
if run_on_real_hardware:
    backend_name = "ibm_fez"
else:
    backend_name = "fake_fez"

# Start a job for empirical time estimation
estimation_job_wo_obp = qesem_function.run(
    pubs=[(circ, obs_list)],
    instance=instance,
    backend_name=backend_name,  # E.g. "ibm_brisbane"
    options={
        # "empirical" - gets actual time estimates without running full mitigation
        "estimate_time_only": "empirical",
        "max_execution_time": 120,  # Limits the QPU time, specified in seconds.
        "default_precision": precision,
    },
)
print(estimation_job_wo_obp.job_id)
print(estimation_job_wo_obp.status())

Output:

17d3828e-9fdb-482e-8e9b-392f3eefe313
DONE
# Get the result object (blocking method).
# Use job.status() in a loop for non-blocking.
# This takes 1-3 minutes
result = estimation_job_wo_obp.result()
print(
    f"Empirical time estimation (sec): {result[0].metadata['time_estimation_sec']}"
)

Output:

Empirical time estimation (sec): 1200

이제 우리는 연산자 역전파(OBP)를 사용할 것입니다. (OBP Qiskit 애드온에 대한 자세한 내용은 OBP 문서를 참조하십시오.) 역전파를 위한 회로 슬라이스를 생성하는 함수를 만들 것입니다:

def run_backpropagation(circ_vec, observable, steps_vec, max_qwc_groups=8):
    """
    Runs backpropagation for a list of circuits and observables.
    Returns lists of backpropagated circuits and observables.
    """
    op_budget = OperatorBudget(max_qwc_groups=max_qwc_groups)
    bp_circuit_vec = []
    bp_observable_vec = []

    for i, circ in enumerate(circ_vec):
        slices = slice_by_gate_types(circ)
        bp_observable, remaining_slices, metadata = backpropagate(
            observable,
            slices,
            operator_budget=op_budget,
        )
        bp_circuit = combine_slices(remaining_slices, include_barriers=True)
        bp_circuit_vec.append(bp_circuit)
        bp_observable_vec.append(bp_observable)
        print(f"n.o. steps: {steps_vec[i]}")
        print(f"Backpropagated {metadata.num_backpropagated_slices} slices.")
        print(
            f"New observable has {len(bp_observable.paulis)} terms, "
            f"which can be combined into "
            f"{len(bp_observable.group_commuting(qubit_wise=True))} groups.\n"
            f"After truncation, the error in our observable is bounded by "
            f"{metadata.accumulated_error(0):.3e}"
        )
        print("-----------------")
    return bp_circuit_vec, bp_observable_vec

함수를 호출합니다:

bp_circ_vec, bp_obs_vec = run_backpropagation([circ], observable, [steps])

Output:

n.o. steps: 9
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
print("The remaining circuit after backpropagation looks as follows:")
bp_circ_vec[-1].draw("mpl", scale=0.8, fold=-1, idle_wires=False)
None

Output:

The remaining circuit after backpropagation looks as follows:
Output of the previous code cell

역전파가 회로의 두 레이어를 줄인 것을 볼 수 있습니다. 이제 축소된 회로와 확장된 관측값이 있으므로 역전파된 회로로 시간 추정을 해보겠습니다:

# Start a job for empirical time estimation
estimation_job_obp = qesem_function.run(
    pubs=[(bp_circ_vec[-1], [bp_obs_vec[-1]])],
    instance=instance,
    backend_name=backend_name,
    options={
        "estimate_time_only": "empirical",
        "max_execution_time": 120,
        "default_precision": precision,
    },
)
print(estimation_job_obp.job_id)
print(estimation_job_obp.status())

Output:

8bae699d-a16b-4d39-bbd9-d123fbcce55d
DONE
result_obp = estimation_job_obp.result()
print(
    f"Empirical time estimation (sec): {result_obp[0].metadata['time_estimation_sec']}"
)

Output:

Empirical time estimation (sec): 900

OBP가 회로 완화에 소요되는 시간 비용을 줄여준다는 것을 알 수 있습니다.


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

실제 백엔드로 실행

이제 몇 가지 트로터 단계에 대한 전체 실험을 실행합니다. 큐비트 수, 필요한 정밀도, 최대 QPU 시간은 사용 가능한 QPU 리소스에 따라 수정할 수 있습니다. 아래 최종 플롯에서 볼 수 있듯이 최대 QPU 시간을 제한하면 최종 정밀도에 영향을 미칩니다.

5, 7, 9 트로터 스텝의 네 가지 회로를 0.05 의 정밀도로 분석하여 이상적인 값, 노이즈가 있는 값, 오류가 완화되는 기대값을 비교합니다:

steps_vec = [5, 7, 9]

circ_vec = []
for steps in steps_vec:
    circ = trotter_circuit_from_layers(
        steps, theta_x, theta_z, theta_zz, layers
    )
    circ_vec.append(circ)

다시 말하지만, 런타임을 줄이기 위해 각 회로에서 OBP를 수행합니다:

bp_circ_vec_35, bp_obs_vec_35 = run_backpropagation(
    circ_vec, observable, steps_vec
)

Output:

n.o. steps: 5
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
n.o. steps: 7
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
n.o. steps: 9
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------

이제 전체 QESEM 작업을 일괄적으로 실행합니다. 각 포인트의 최대 QPU 런타임을 제한하여 QPU 예산을 더 잘 관리할 수 있습니다.

run_on_real_hardware = True

precision = 0.05
if run_on_real_hardware:
    backend_name = "ibm_marrakesh"
else:
    backend_name = "fake_fez"
# Running full jobs for:
pubs_list = [
    [(bp_circ_vec_35[i], bp_obs_vec_35[i])] for i in range(len(bp_obs_vec_35))
]
# Initiating multiple jobs for different lengths
job_list = []
for pubs in pubs_list:
    job_obp = qesem_function.run(
        pubs=pubs,
        instance=instance,
        backend_name=backend_name,  # E.g. "ibm_brisbane"
        options={
            "max_execution_time": 300,  # Limits the QPU time, specified in seconds.
            "default_precision": 0.05,
        },
    )
    job_list.append(job_obp)

여기에서 각 작업의 상태를 확인합니다:

for job in job_list:
    print(job.status())

Output:

DONE
DONE
DONE
DONE

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

모든 작업의 실행이 완료되면 잡음과 완화된 기대값을 비교할 수 있습니다.

ideal_values = []
noisy_values = []
error_mitigated_values = []
error_mitigated_stds = []

for i in range(len(job_list)):
    job = job_list[i]
    result = job.result()  # Blocking - takes 3-5 minutes
    noisy_results = result[0].metadata["noisy_results"]

    ideal_val = calculate_ideal_evs(circ_vec[i], observable, n_qubits, i)
    print("---------------------------------")
    print(f"Ideal: {ideal_val}")
    print(f"Noisy: {noisy_results.evs}")
    print(f"QESEM: {result[0].data.evs} \u00b1 {result[0].data.stds}")

    ideal_values.append(ideal_val)
    noisy_values.append(noisy_results.evs)
    error_mitigated_values.append(result[0].data.evs)
    error_mitigated_stds.append(result[0].data.stds)

Output:

Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.79537
Noisy: 0.7039237951821501
QESEM: 0.7828018244130982 ± 0.013257266977728376
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.78653
Noisy: 0.6478583812958806
QESEM: 0.7875259197423828 ± 0.02703045139248604
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.79699
Noisy: 0.6171787879868142
QESEM: 0.6918791909168913 ± 0.0740873782039517

마지막으로 자화 대 걸음 수를 그래프로 그릴 수 있습니다. 잡음이 많은 양자 디바이스에서 편향 없는 오류 완화를 위해 QESEM 키스킷 함수를 사용하면 얻을 수 있는 이점이 요약되어 있습니다.

plt.plot(steps_vec, ideal_values, "--", label="ideal")
plt.scatter(steps_vec, noisy_values, label="noisy")
plt.errorbar(
    steps_vec,
    error_mitigated_values,
    yerr=error_mitigated_stds,
    fmt="o",
    capsize=5,
    label="QESEM mitigation",
)
plt.legend()
plt.xlabel("n.o. steps")
plt.ylabel("Magnetization")

Output:

Text(0, 0.5, 'Magnetization')
Output of the previous code cell
Note

아홉 번째 단계의 통계 오차 막대가 큰 이유는 QPU 시간을 5분으로 제한했기 때문입니다. 이 단계를 15분 동안 실행하면(경험적 시간 추정치에서 제안하는 대로) 오차 막대가 더 작아집니다. 따라서 완화된 값은 이상적인 값에 가까워집니다.

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