주 콘텐츠로 건너뛰기

클래식 피드포워드와 제어 흐름

패키지 버전

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

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
동적 Circuit이 이제 모든 Backend에서 사용 가능합니다

동적 Circuit의 새 버전이 이제 모든 사용자와 모든 Backend에서 사용 가능합니다. 이제 유틸리티 규모로 동적 Circuit을 실행할 수 있습니다. 자세한 내용은 공지 사항을 참조하세요.

동적 Circuit은 강력한 도구로, 양자 Circuit 실행 중에 Qubit을 측정하고 해당 중간 Circuit 측정 결과를 바탕으로 Circuit 내에서 클래식 논리 연산을 수행할 수 있습니다. 이 과정은 _클래식 피드포워드_라고도 알려져 있습니다. 동적 Circuit을 어떻게 활용할지에 대한 이해는 아직 초기 단계이지만, 양자 연구 커뮤니티는 이미 다음과 같은 여러 활용 사례를 발굴했습니다.

그러나 동적 Circuit이 가져오는 이러한 개선에는 트레이드오프가 있습니다. 중간 Circuit 측정과 클래식 연산은 일반적으로 2-Qubit Gate보다 실행 시간이 더 길며, 이 시간 증가가 Circuit 깊이 감소의 이점을 상쇄할 수 있습니다. 따라서 IBM Quantum®이 동적 Circuit의 새 버전을 출시하면서 중간 Circuit 측정 시간 단축이 주요 개선 목표가 되고 있습니다.

OpenQASM 3 명세는 다양한 제어 흐름 구조를 정의하고 있지만, Qiskit Runtime은 현재 조건부 if 문만 지원합니다. Qiskit SDK에서 이는 QuantumCircuitif_test 메서드에 해당합니다. 이 메서드는 컨텍스트 매니저를 반환하며, 일반적으로 with 문에서 사용됩니다. 이 가이드는 이 조건문을 사용하는 방법을 설명합니다.

참고

이 가이드의 코드 예제는 중간 Circuit 측정에 표준 측정 명령을 사용합니다. 그러나 Backend가 지원하는 경우 MidCircuitMeasure 명령을 대신 사용하는 것을 권장합니다. 자세한 내용은 중간 Circuit 측정 문서를 참조하세요.

if

if 문은 클래식 비트 또는 레지스터의 값을 기반으로 조건부로 연산을 수행하는 데 사용됩니다.

아래 예제에서는 Qubit에 Hadamard Gate를 적용하고 측정합니다. 결과가 1이면 Qubit에 X Gate를 적용하여 0 상태로 되돌립니다. 그런 다음 Qubit을 다시 측정합니다. 최종 측정 결과는 100% 확률로 0이 됩니다.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

circuit.h(q0)
# Use MidCircuitMeasure() if it's supported by the backend.
# circuit.append(MidCircuitMeasure(), [q0], [c0])
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")

# example output counts: {'0': 1024}

이전 코드 셀의 출력

with 문에는 컨텍스트 매니저 자체인 할당 대상을 지정할 수 있으며, 이를 저장해 두었다가 나중에 else 블록을 만드는 데 사용할 수 있습니다. else 블록은 if 블록의 내용이 실행되지 않을 때마다 실행됩니다.

아래 예제에서는 두 Qubit과 두 클래식 비트로 레지스터를 초기화합니다. 첫 번째 Qubit에 Hadamard Gate를 적용하고 측정합니다. 결과가 1이면 두 번째 Qubit에 Hadamard Gate를 적용하고, 그렇지 않으면 두 번째 Qubit에 X Gate를 적용합니다. 마지막으로 두 번째 Qubit도 측정합니다.

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q1)
with else_:
circuit.x(q1)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 260, '11': 272, '10': 492}

이전 코드 셀의 출력

단일 클래식 비트에 조건을 거는 것 외에도, 여러 비트로 구성된 클래식 레지스터의 값에 조건을 거는 것도 가능합니다.

아래 예제에서는 두 Qubit에 Hadamard Gate를 적용하고 측정합니다. 결과가 01, 즉 첫 번째 Qubit이 1이고 두 번째 Qubit이 0이면, 세 번째 Qubit에 X Gate를 적용합니다. 마지막으로 세 번째 Qubit을 측정합니다. 명확성을 위해 세 번째 클래식 비트의 상태(0)를 if 조건에 명시적으로 지정했습니다. Circuit 다이어그램에서 조건은 조건이 걸린 클래식 비트의 원으로 표시됩니다. 검은 원은 1에 대한 조건을, 흰 원은 0에 대한 조건을 나타냅니다.

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}

이전 코드 셀의 출력

클래식 표현식

Qiskit 클래식 표현식 모듈 qiskit.circuit.classical은 Circuit 실행 중 클래식 값에 대한 런타임 연산의 탐색적 표현을 포함합니다. 하드웨어 제한으로 인해 현재는 QuantumCircuit.if_test() 조건만 지원됩니다.

다음 예제는 패리티 계산을 사용하여 동적 Circuit으로 n-Qubit GHZ 상태를 생성하는 방법을 보여줍니다. 먼저 인접한 Qubit에 n/2n/2개의 Bell 쌍을 생성합니다. 그런 다음 쌍 사이에 CNOT Gate 레이어를 사용하여 이 쌍들을 연결합니다. 이전 CNOT Gate의 타겟 Qubit을 모두 측정하고 측정된 각 Qubit을 0\vert 0 \rangle 상태로 초기화합니다. 이전 모든 비트의 패리티가 홀수인 측정되지 않은 각 사이트에 XX를 적용합니다. 마지막으로 측정된 Qubit에 CNOT Gate를 적용하여 측정에서 손실된 얽힘을 재확립합니다.

패리티 계산에서 구성된 표현식의 첫 번째 요소는 Python 객체 mr[0]Value 노드로 들어올리는 것입니다 (lift는 임의의 객체를 클래식 표현식으로 변환하는 데 사용됩니다). mr[1]과 이후의 가능한 클래식 레지스터에는 이것이 필요하지 않습니다. 이들은 expr.bit_xor의 입력이며, 필요한 변환은 이 경우에 자동으로 수행됩니다. 이러한 표현식은 루프 및 다른 구조에서 점차적으로 구성할 수 있습니다.

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2)) # qubits to measure and reset

qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)

# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
qc.measure(qr[q], mr[k])
qc.reset(qr[q])

# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
if tgt in meas_qubits: # skip measured qubits
continue
# all measurement registers whose physical qubit index < tgt
left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
if not left_bits: # skip if list empty
continue

# build XOR-parity expression
parity = expr.lift(
mr[left_bits[0]]
) # lift the first bit to Value so it will be treated like a boolean.
for k in left_bits[1:]:
parity = expr.bit_xor(
mr[k], parity
) # calculate parity with all other bits
with qc.if_test(parity): # Add X if parity is 1
qc.x(qr[tgt])

# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)

이전 코드 셀의 출력

동적 Circuit을 지원하는 Backend 찾기

계정에서 액세스할 수 있고 동적 Circuit을 지원하는 모든 Backend를 찾으려면 다음과 같은 코드를 실행하세요. 이 예제는 로그인 자격 증명을 저장했다고 가정합니다. Qiskit Runtime 서비스 계정을 초기화할 때 명시적으로 자격 증명을 지정할 수도 있습니다. 예를 들어 특정 인스턴스나 플랜 유형에서 사용 가능한 Backend를 볼 수 있습니다.

참고
  • 계정에서 사용 가능한 Backend는 자격 증명에 지정된 인스턴스에 따라 다릅니다.
  • 동적 Circuit의 새 버전이 이제 모든 사용자와 모든 Backend에서 사용 가능합니다. 자세한 내용은 공지 사항을 참조하세요.
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_miami')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_torino')>, <IBMBackend('ibm_kingston')>]

Qiskit Runtime 제한 사항

Qiskit Runtime에서 동적 Circuit을 실행할 때 다음 제약 사항에 주의하세요.

  • 제어 전자 장치의 물리적 메모리 제한으로 인해 if 문의 수와 피연산자 크기에도 제한이 있습니다. 이 제한은 작업(Circuit이 아님)에서 브로드캐스트 수와 브로드캐스트된 비트 수의 함수입니다.

    if 조건을 처리할 때 해당 평가를 위해 측정 데이터를 제어 로직으로 전송해야 합니다. 브로드캐스트는 고유한 클래식 데이터의 전송이며, 브로드캐스트된 비트는 전송되는 클래식 비트의 수입니다. 다음을 고려하세요.

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    이전 코드 예제에서 c0에 대한 처음 두 if_test 객체는 c0의 내용이 변경되지 않았으므로 재브로드캐스트할 필요가 없어 하나의 브로드캐스트로 간주됩니다. c1에 대한 if_test는 두 번째 브로드캐스트입니다. 첫 번째는 c0의 세 비트를 모두 브로드캐스트하고 두 번째는 한 비트만 브로드캐스트하여 총 4개의 브로드캐스트된 비트가 됩니다.

    현재, 매번 60비트를 브로드캐스트하면 작업에 약 300번의 브로드캐스트가 가능합니다. 그러나 매번 1비트만 브로드캐스트하면 작업에 2400번의 브로드캐스트가 가능합니다.

  • if_test 문에 사용되는 피연산자는 32비트 이하여야 합니다. 따라서 전체 ClassicalRegister를 비교하는 경우 해당 ClassicalRegister의 크기는 32비트 이하여야 합니다. 그러나 ClassicalRegister에서 단일 비트만 비교하는 경우에는 피연산자가 1비트이므로 해당 ClassicalRegister의 크기에 제한이 없습니다.

    예를 들어, 아래 "유효하지 않음" 코드 블록은 cr이 32비트를 초과하므로 작동하지 않습니다. 그러나 "유효함" 코드 블록에서 보여주듯이 단일 비트만 테스트하는 경우에는 32비트보다 넓은 클래식 레지스터를 사용할 수 있습니다.

       cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • 중첩 조건문은 허용되지 않습니다. 예를 들어, 다음 코드 블록은 if_test 안에 다른 if_test가 있으므로 작동하지 않습니다.

       c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • 조건문 내부에 reset 또는 측정을 사용하는 것은 지원되지 않습니다.

  • 산술 연산은 지원되지 않습니다.

  • Qiskit 및 Qiskit Runtime에서 지원되는 OpenQASM 3 기능을 확인하려면 OpenQASM 3 기능 표를 참조하세요.

  • OpenQASM 3(대신 QuantumCircuit)이 Qiskit Runtime 프리미티브에 Circuit을 전달하는 입력 형식으로 사용될 때는 Qiskit에 로드할 수 있는 명령만 지원됩니다. 예를 들어, 클래식 연산은 Qiskit에 로드할 수 없으므로 지원되지 않습니다. 자세한 내용은 OpenQASM 3 프로그램을 Qiskit으로 가져오기를 참조하세요.

  • for, while, switch 명령은 지원되지 않습니다.

Estimator와 함께 동적 Circuit 사용하기

Estimator는 동적 Circuit을 지원하지 않으므로, 대신 Sampler를 사용하여 직접 측정 Circuit을 구성할 수 있습니다. 또는 동적 Circuit을 지원하는 Executor 프리미티브를 사용할 수 있습니다.

Estimator의 동작을 재현하려면 다음 과정을 따르세요.

  1. 모든 관측 가능량의 항을 분할로 묶습니다. 예를 들어 PauliList API를 사용하여 수행할 수 있습니다.
    참고

    BitArray 프리미티브 속성을 사용하여 제공된 관측 가능량의 기댓값을 계산할 수 있습니다.

  2. 분할당 하나의 기저 변환 Circuit을 실행합니다(각 분할에 수행해야 하는 기저 변환). 자세한 내용은 측정 기저 애드온 유틸리티 measurement_bases 모듈을 참조하세요. 유틸리티 시작하기.
  3. 각 분할의 결과를 다시 합산합니다.

다음 단계

권장 사항
  • stretch를 사용하여 정확한 동적 디커플링을 구현하는 방법을 알아보세요.
  • Circuit 시간을 줄이는 더 짧은 중간 Circuit 측정에 대해 알아보세요.
  • Circuit 스케줄 시각화를 사용하여 동적 Circuit을 디버그하고 최적화하세요.