Skip to main content
IBM Quantum Platform

역전파 과정에서 파울리 항을 잘라내기

이 가이드에서는 qiskit_addon_obp.utils.truncating 모듈에서 제공하는 파울리 항 절단 메커니즘을 구성하는 방법을 설명합니다.

연산자 역전파(OBP)를 활용하면, 관측량의 복잡성이 증가하는 대가로 양자 회로의 깊이를 줄일 수 있다. OBP에서 의미 있는 결과를 얻기 위해서는, 일반적으로 관측 가능한 변수의 항이 지나치게 커지는 것을 방지하기 위해 해당 항들을 잘라내야 합니다. 연산자가 지나치게 커지는 것을 방지하면서도 회로 내로 더 깊이 역전파가 이루어지도록 하는 한 가지 방법은, 계수가 작은 항들을 연산자에 더하는 대신 잘라내는 것입니다. 항을 잘라내면 실행해야 할 양자 회로의 수가 줄어들 수 있지만, 이렇게 할 경우 잘라낸 항의 계수 크기에 비례하는 오류가 최종 기대값 계산에 발생하게 된다.

백프로파게이션 메서드는 선택적 매개변수인 TruncationErrorBudget을 받으며, 이 매개변수는 각 슬라이스의 백프로파게이션이 성공적으로 완료된 후 각 관측량에 대해 가중치가 낮은 파울리 항의 절삭을 구성합니다. 잘리는 항의 개수는 사용자가 지정한 다양한 구성 매개변수에 따라 달라집니다. 현재로서는 ‘truncate_binary_search ’ 메서드라는 단 하나의 잘라내기 전략만 사용할 수 있습니다. 주어진 관측량과 예산 에 따라, 이 프로그램은 해당 관측량 내의 파울리 항과 계수에 대해 이진 탐색을 수행하여, 잘린 계수들의 합이 최대이면서 예산보다 작은 최적의 임계값을 찾습니다.

참고 : 기본적으로 절단 오차를 평가하고 상한을 설정하는 데 L1 노름이 사용되지만, 이 설정을 p_norm 활성화하면 사용할 Lp-노름을 지정할 수 있습니다. 해당 설정의 사용 방법에 대한 자세한 내용은 ‘파울리 항 절단을 위한 서로 다른 Lp-노름 사용 가이드를 참조하십시오.

다음 예제는 함께 제공되는 setup_budget 함수를 사용하여 TruncationErrorBudget 를 구성하는 다양한 방법을 보여줍니다.


예제 회로를 구성해 보세요

이 가이드에서는 다음 회로 슬라이스를 사용합니다:

import rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
    PauliOrderStrategy,
    generate_time_evolution_circuit,
    generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types

# Generate a linear chain of 10 qubits
linear_chain = rustworkx.generators.path_graph(10)

# Use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
    linear_chain,
    coupling_constants=(0.05, 0.02, 0.0),
    ext_magnetic_field=(0.02, 0.08, 0.0),
    pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# Evolve for some time
circuit = generate_time_evolution_circuit(
    hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)

# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)

Output:

Output of the previous code cell

여기서는 하나의 간단한 관측량을 살펴보겠습니다:

from qiskit.quantum_info import SparsePauliOp

obs = SparsePauliOp("IIIIIZIIII")

가장 간단한 경우: 각 슬라이스에 대해 고정된 잘라내기 할당량

파울리 항의 생략에 할당되는 예산은 역전파의 각 단계마다 달라질 수 있다. 이것이 어떻게 작동하는지 이해하기 위해, 먼저 사용자가 지정한 고정된 절단 예산이라는 가장 간단한 경우를 살펴보겠습니다.

잘림 예산을 지정하는 가장 간단한 방법은 인수를 max_error_per_slice 사용하는 것입니다. 사실, ‘연산자 역전파를 이용한 회로 깊이 줄이기’ 튜토리얼에서 바로 이러한 방식이 사용되고 있습니다. 를 로 max_error_per_slice 설정하면 각 슬라이스에 해당 값과 동일한 예산이 할당됩니다 float . 아래 예시에서, 이 값을 로 0.001 설정하면 18개의 슬라이스 모두에 대해 역전파가 수행될 경우 암시적 절단 오차가 최대 로 제한됩니다 0.018.

슬라이스에 대해 역전파를 수행하고 계수가 작은 항들을 잘라낸 후 남은 오차 예산은 항상 다음 슬라이스의 오차 예산에 더해진다는 점에 유의하십시오.

from qiskit_addon_obp.utils.truncating import setup_budget

truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget

op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 11 circuit slices.
New observable contains 29 terms and 10 commuting groups.

다음으로 OBPMetadata 인스턴스와 시각화 모듈에서 제공하는 도구를 사용하여 역전파 프로세스를 시각화합니다.

  • 왼쪽 상단의 그래프에서 볼 수 있듯이, 세 번째 슬라이스에 대한 역전파를 수행한 후 관측 가능한 항들을 잘라내기 시작할 만큼 충분한 예산이 확보된 것으로 나타납니다. 세 번째 슬라이스부터는, 각 슬라이스마다 역전파를 수행할 때마다 적어도 하나의 항이 잘려나간다는 것을 알 수 있습니다. 이는 각 슬라이스 처리 후 일정한 절단 오차가 발생하기 때문입니다.
  • 오른쪽 상단의 그래프는 세 번째 슬라이스의 경우 .003 오차 예산이 까지 증가함을 보여줍니다. 잔여 예산이 급격히 감소한 것을 볼 수 있는데, 이는 관측 가능한 데이터에서 조건들이 생략되었음을 의미합니다. 이는 왼쪽 상단 그래프에서 우리가 추론한 내용과 일치합니다.
  • 왼쪽 하단의 그래프에서 볼 수 있듯이, 관측 가능한 함수에서 항들을 제거해 나갈수록 전체 누적 오차는 단조 증가하는 양상을 보입니다. 이 그래프는 또한 세 번째 슬라이스에 대한 역전파가 완료될 때까지 어떤 항도 잘리지 않았음을 보여줍니다.
  • 오른쪽 하단의 그래프는 우리 관측량에서 통근 파울리 군의 수가 지정된 한계인 까지 증가했음을 보여줍니다 10. 이 그래프는 또한 검은색 선과 빨간색 선이 교차하는 부분에서 볼 수 있듯이, 백프로파게이션을 한 층 더 수행하면 관측값이 지정된 범위를 벗어나게 된다는 점을 보여줍니다.

이 모든 그래프에서 x축은 역전파된 슬라이스를 나열하고 있지만, OBP는 회로의 끝부분에서 작동하므로 slice 1 는 맨 마지막 슬라이스이고, slice 2 는 그 바로 앞의 슬라이스이며, 이와 같은 순서로 이어진다는 점에 유의하십시오.

from matplotlib import pyplot as plt
from qiskit_addon_obp.utils.visualization import (
    plot_accumulated_error,
    plot_left_over_error_budget,
    plot_num_qwc_groups,
    plot_slice_errors,
)

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

슬라이스 예산을 명시적으로 지정합니다

역전파 성능이 최적화되도록 각 슬라이스에 예산을 할당하는 방법을 알고 있다면, 각 슬라이스에 대해 예산을 명시적으로 할당하는 것이 좋습니다. 설명하기 위해, 처음 세 슬라이스에는 예산을 0으로 설정하고, 나머지 슬라이스에는 슬라이스당 .001 예산이 할당되도록 하겠습니다.

# Zero out the first 3 slices' budgets
max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)

truncation_error_budget = setup_budget(
    max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 11 circuit slices.
New observable contains 32 terms and 10 commuting groups.

처음 세 계층에서 예산 할당을 제거한 결과, 네 번째 계층 이후까지는 어떤 용어도 잘리지 않았으며, 이는 이 그래프 중 세 개에서 확인할 수 있습니다. 다소 놀랍게도, 네 번째 구간에는 이월된 예산이 전혀 배정되지 않았음에도 불구하고, 할당된 예산을 .001 사용하여 한 학기가 단축되었습니다. 이는 왼쪽 상단 그래프에서 명백히 드러나지만, 오른쪽 상단 그래프에서도 3번과 4번 슬라이스 사이에서 잔여 예산 곡선이 완만해지는 것을 확인할 수 있습니다. 또 다른 주목할 만한 점은, 이전 예시와 마찬가지로 이 시점부터 적어도 하나의 항이 잘려 나갔다는 것이다.

핵심 요점은, 두 번째 예시에서는 일부 슬라이스의 예산을 0으로 설정함으로써 절단 오차가 줄어들긴 했지만, 동일한 수의 슬라이스에 대해 역전파를 수행할 수 있었으며, 관측 가능량에는 동일한 수의 통근 파울리 군이 포함되어 있다는 점입니다. 왼쪽 아래 그래프를 살펴보면, 두 번째 예시에서 오차의 상한이 더 작다는 것을 확인할 수 있습니다.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

예산은 주기적으로 책정하십시오

트로터 회로와 같이 반복되는 패턴이 있는 회로의 경우, 해당 반복되는 슬라이스 하위 집합에 대한 예산을 지정하고, 그 예산이 해당 슬라이스의 이후 모든 반복에 사용되도록 할 수 있습니다.

좀 더 구체적으로 말하자면, 우리가 사용하고 있는 예제 회로에는 6개의 슬라이스가 3번 반복되어 총 18개의 슬라이스가 있습니다. 단일 큐비트 층과 RYY 층에는 임의로 예산 0을 할당하고, RXX 층 각각에는 .003 예산씩 할당할 것입니다. 예산을 length-6 시퀀스로 지정하면, 예산이 18개의 모든 슬라이스에 순환적으로 적용되는 방식을 살펴보겠습니다.

다시 한 번, 슬라이스들에 대한 역전파가 역순으로(즉, 끝 부분부터 시작하여) 이루어진다는 점에 유의하십시오. 따라서, 순환 예산의 첫 번째 항목은 실제로 마지막 슬라이스에 사용되고, 두 번째 항목은 그 바로 앞의 슬라이스에 사용되며, 이와 같은 방식으로 이어집니다.

# Specify a length-6 per-slice budget.
# This will be cycled over three times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2

truncation_error_budget = setup_budget(
    max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)
op_budget = OperatorBudget(max_qwc_groups=20)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 13 circuit slices.
New observable contains 49 terms and 14 commuting groups.

왼쪽 상단과 왼쪽 하단 이미지에서 볼 수 있듯이, 처음 네 개의 슬라이스에는 오차 허용량이 할당되지 않았기 때문에 잘림 처리가 수행되지 않았습니다. 5번과 6번 단계에서는 예산이 확보됨에 따라 일부 용어가 축약되었습니다.

왼쪽 상단 이미지에서 볼 수 있듯이, 7번 슬라이스에는 예산이 할당되지 않았음에도 불구하고, 해당 슬라이스에 대해 역전파를 수행한 후 비교적 큰 오차가 발생했습니다. 이는 5번과 6번 슬라이스에 배정된 총 .006 예산 중 .002 약 만 사용되었기 때문이며, 따라서 나머지 예산은 7번 슬라이스로 이관되어 우측 상단 이미지에서 볼 수 있듯이 대부분 소진되었기 때문입니다.

왼쪽 상단과 오른쪽 상단의 이미지에서 볼 수 있듯이, 예상대로 소량의 잔여 예산은 8~10번 슬라이스 사이에서 소진되고, 11번 슬라이스에서는 새로운 예산이 할당됩니다. 왼쪽 위, 오른쪽 위, 왼쪽 아래의 그림들은 모두 max_error_per_slice 인수의 길이가 슬라이스 수보다 작을 때 나타나는 주기적인 거동을 보여줍니다. 이러한 주기적인 현상은 모든 슬라이스에 걸쳐 계속되었을 것이지만, 오른쪽 아래 이미지에서 볼 수 있듯이 13개의 슬라이스에 대해 역전파를 수행한 후 중단 max_qwc_groups 기준에 도달했습니다.

또한 흥미로운 점은, 슬라이스 11에서 두 번째 예산 배정이 이루어진 후 파울리 그룹의 수가 실제로 감소한다는 사실입니다. 이는 계수가 작은 그룹들이 슬라이스 11에 대한 역전파가 완료되어 예산이 더 확보될 때까지 잘라낼 수 없었기 때문에, 여러 반복 과정에 걸쳐 관측 가능량에 누적되었기 때문입니다. 또한 이 사례는 알고리즘이 종료되기 위해서는 를 초과 해야 max_qwc_groups 한다는 점을 보여줍니다.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

총 오차 상한 설정

슬라이스별 오차 허용 한도를 지정하는 것 외에도, 절단으로 인해 발생할 수 있는 최대 오차량을 지정할 수 있습니다. 해당 한도에 도달하면 더 이상 절삭 처리는 수행되지 않지만, 관측값이 너무 커져 중단 기준 중 하나가 충족될 때까지 역전파는 계속됩니다.

다음으로, 오차 예산이 7번째 슬라이스의 역전파가 끝난 후에 소진되도록 max_error_total 에 상한을 설정하고, 위의 실험을 다시 수행합니다.

# Specify a length-6 per-slice budget.
# This will be cycled over 3 times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2

truncation_error_budget = setup_budget(
    max_error_per_slice=max_error_per_slice, max_error_total=0.006
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 10 circuit slices.
New observable contains 67 terms and 20 commuting groups.

예상대로, 우리의 절단 오차는 ( .006** 왼쪽 아래** 그래프)에서 상한에 도달합니다. 이번 실행에서 슬라이스 11에 대해 역전파를 수행하지 못했다는 점이 주목할 만합니다. 이는 항을 잘라낼 만큼의 예산이 충분하지 않았고 20, 오른쪽 아래 그림에서 볼 수 있듯이 통근 파울리 군의 수가 한계를 초과했기 때문입니다.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

전체 예산에 상한선을 설정하는 것만으로도 충분할 수 있으며, 구체적인 금액을 명시하지 max_error_total 않아도 됩니다 max_error_per_slice.

truncation_error_budget = setup_budget(max_error_total=0.018)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)

위의 셀에 표시된 결과는 약간 의외일 수 있는데, 이는 가 로 설정되어 있기 per_slice_budget 때문입니다 max_error_total. 이는 사용 가능한 예산 전액이 아낌없이 소진될 것임을 시사합니다. 이렇게 생각해 볼 수 있습니다. 각 슬라이스에는 전체 예산이 할당됩니다(왜냐하면 우리는 를 반복하기 때문입니다 per_slice_budget). 그러나 이미 소진된 예산은 알고리즘의 해당 시점에서 사용 가능한 예산에서 차감됩니다.

op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 9 circuit slices.
New observable contains 25 terms and 9 commuting groups.

오른쪽 상단 이미지는 전체 오차 .018 예산이 첫 번째 슬라이스에 어떻게 할당되는지를 보여줍니다. 세 번째 슬라이스까지는 잘림 처리가 이루어지지 않으므로, 남은 예산은 일정하게 유지됩니다. 예산은 소진될 때까지 각 역전파된 슬라이스에 전체 예산이 할당됨에 따라 단조 감소합니다.

이 실험에서, 이 노트북의 첫 번째 실험과 거의 동일함에도 불구하고 역전파된 슬라이스 수가 두 개 더 적게 나온 점은 주목할 만하다. 이는 일부 문제의 경우, 예산을 균등하게 배분하는 것이 최선의 방법일 수 있음을 보여준다. 그 외의 문제의 경우, 슬라이스가 전체 예산을 최대한 많이 사용하도록 허용하면 더 나은 성능을 얻을 수 있습니다.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

역전파되는 슬라이스 수와 총 오차를 함께 제한합니다

오류 예산을 회로 전체에 걸쳐 분배하고 싶지는 않지만, 그렇다고 무분별하게 소모하고 싶지도 않은 경우, 역전파할 슬라이스 수(num_slices)와 총 오류 예산(max_error_total)을 함께 지정할 수 있습니다. 이렇게 하면 오류 예산이 (에 따라 p_norm) 입력 슬라이스 전체에 균등하게 분배됩니다.

여기서는 역전파할 수 있는 슬라이스 수를 로 제한하고 12, 총 오차 예산은 그대로 유지하겠습니다.

num_slices = 12

truncation_error_budget = setup_budget(
    max_error_total=0.018, num_slices=num_slices, p_norm=1
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)

이제 이전 단계에서 예산을 할당했던 12개의 슬라이스에 대해 역전파를 시도해 보겠습니다. 이를 위해 회로에 마지막 12개의 슬라이스만 전달하면 됩니다. 의 경우 p_norm=1, 12개의 슬라이스 각각에 할당 가능한 예산은 입니다 0.018 / num_slices = 0.0015.

bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices[-num_slices:],
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)

(코드 내 slices[-num_slices:]) 슬라이스의 일부를 에 전달했기 때문에 backpropagate, 역전파 후 남은 슬라이스를 역전파에 전달되지 않았던 슬라이스(코드 내) slices[:-num_slices]와 결합해야 합니다.

남은 모든 슬라이스를 결합한 후에는 combine_slices를 사용하여 깊이가 축소된 QuantumCircuit 를 생성할 수 있습니다. 다음으로, 관측 가능한 변수의 크기가 얼마나 커졌는지에 비해 역전파된 슬라이스가 몇 개나 되는지 살펴봅니다.

# Recombine the slices remaining after backprop with the rest of the original circuit
reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)

print(f"Backpropagated {num_slices - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 12 circuit slices.
New observable contains 29 terms and 9 commuting groups.

그래프를 보면, 관측 가능한 변수를 10개의 통행 가능한 파울리 군 이내로 유지하면서 12개의 슬라이스 모두에 대해 역전파를 성공적으로 수행했음을 알 수 있습니다. 또한 와 를 함께 num_slices 사용하면 슬라이스 간에 예산이 분배되며 max_error_total , 미사용 예산은 다시 다음 슬라이스로 이월된다는 것을 알 수 있습니다. 이는 오른쪽 상단의 그래프에서 가장 뚜렷하게 드러나는데, 역전파 과정 전반에 걸쳐 예산이 소진되기도 하고 다시 보충되기도 하기 때문이다.

이 오차 분배 방식이 이 노트북의 첫 번째 실험 및 바로 앞 예제와 비교했을 때 가장 좋은 결과(역전파된 슬라이스 수가 더 많음)를 보였다는 점은 주목할 만합니다. 이 모든 예시에서는 오차 .018 예산을 할당했지만, 그 예산이 어떻게 분배되었는지에 따라 역전파의 수행 방식이 달랐습니다.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

여러 관측 가능 객체와 함께 작업하기

qiskit_addon_obp.backpropagate 메서드를 사용하면 일련의 관측 가능 객체를 전달할 수 있습니다. 이를 통해 여러 대상 관측량을 다룰 때의 작업 흐름이 간소화됩니다.

이곳에서 이를 명시적으로 언급하는 이유는, 잘림 전략이 이러한 경우를 어떻게 처리하는지 설명해 드리기 위함입니다. 이 예제를 위해, 지금까지 사용해 온 관측 가능 변수에 하나 더 추가합니다:

obs = [SparsePauliOp("IIIIIZIIII"), SparsePauliOp("IIIIIXIIII")]

마지막으로, 이 튜토리얼의 첫 번째 실험을 다시 수행합니다., 하지만 역전파를 수행할 두 개의 관측량이 있는 회로입니다.

이 예제의 경우, 이는 역전파될 수 있는 슬라이스의 수에는 영향을 미치지 않습니다. 그러나 두 관측량에서는 파울리 항의 개수와 교환군의 개수가 서로 달랐음을 알 수 있다.

truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups."
)
print(
    f"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 11 circuit slices.
The new first observable contains 29 terms and 10 commuting groups.
The new second observable contains 23 terms and 8 commuting groups.

아래의 그림들은 역전파 알고리즘이 여러 관측 변수를 어떻게 처리하는지 설명해 줍니다.

먼저, 왼쪽 위, 오른쪽 위, 왼쪽 아래 의 그래프에서 볼 수 있듯이, 항을 생략하기 위한 예산은 관측 변수마다 개별적으로 설정됩니다. 다시 말해, 두 관측 가능량 모두 역전파된 슬라이스당 0.001 의 오차를 가정하여 항을 잘라낼 수 있는 능력을 가지고 있다. 관측 가능한 변수들의 특성이 서로 다르기 때문에, 이로 인해 예산 소모량에도 차이가 발생합니다. 이 예시에서는 두 가지가 상당히 겹치는 모습을 보이지만, 항상 그런 것은 아닙니다.

오른쪽 아래의 그래프는 이 함수가 모든 관측 가능한 변수를 모두 고려하고 max_qwc_groups 있음을 보여줍니다. 즉, 모든 관측 가능한 양의 항들을 한데 묶어 큐비트 단위로 교환하는 군의 최종 개수를 산출한 뒤, 이를 와 비교합니다 max_qwc_paulis. max_paulis 임계값(이 노트북에서는 다루지 않음)에 대해서도 동일한 처리가 이루어지며, 이를 통해 모든 관측량에 걸친 파울리 항의 개수에 제한을 설정할 수 있습니다.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

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