Skip to main content
IBM Quantum Platform

고전적 피드포워드 및 제어 흐름(동적 회로)

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

    qiskit[all]~=2.5.2
    

동적 회로는 양자 회로 실행 중간에 큐비트를 측정한 다음 이러한 중간 회로 측정 결과를 기반으로 회로 내에서 고전적인 논리 연산을 수행할 수 있는 강력한 도구입니다. 이 프로세스는 고전적인 피드포워드라고도 합니다. 동적 회로를 가장 잘 활용하는 방법을 이해하는 초기 단계이지만, 양자 연구 커뮤니티에서는 이미 다음과 같은 여러 가지 사용 사례를 확인했습니다:

QuantumCircuitQiskit은 고전적인 피드포워드(feedforward)를 위한 네 가지 제어 흐름 구조를 지원하며, 각각은 의 메서드로 구현되어 있습니다. 구성과 이에 대응하는 메서드는 다음과 같습니다:

with 이 방법들은 각각 컨텍스트 매니저 를 반환하며, 일반적으로 문에서 사용됩니다. 이 가이드의 나머지 부분에서는 이러한 각 구문과 그 사용법을 설명합니다.

Caution

양자 하드웨어에서 고전적인 피드포워드 및 제어 흐름 연산에는 프로그램에 영향을 미칠 수 있는 몇 가지 제한 사항이 있습니다. 자세한 내용은 ‘동적 회로 실행’을 참조하십시오.


if 명령문

if 문은 기존 비트 또는 레지스터 값에 따라 조건부로 연산을 수행하는 데 사용됩니다.

아래 예시에서는 큐비트에 하다마드 게이트를 적용하고 측정합니다. 결과가 1이면 큐비트에 X 게이트를 적용하여 0 상태로 다시 뒤집는 효과를 냅니다. 그런 다음 큐비트를 다시 측정합니다. 측정 결과는 100% 확률로 0이 되어야 합니다.

from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

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

circuit.h(q0)
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}

Output:

Output of the previous code cell

with 문에는 그 자체가 컨텍스트 관리자인 할당 대상을 지정할 수 있으며, 이 할당 대상은 저장된 후 if 블록의 내용이 실행되지 않을 때마다 실행되는 else 블록을 생성하는 데 사용될 수 있습니다.

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

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}

Output:

Output of the previous code cell

단일 클래식 비트에 대한 컨디셔닝 외에도 여러 비트로 구성된 클래식 레지스터의 값을 컨디셔닝할 수도 있습니다.

아래 예시에서는 두 큐비트에 하다마르 게이트를 적용하고 이를 측정합니다. 결과가 01, 즉 첫 번째 큐비트가 1이고 두 번째 큐비트가 0인 경우, 우리는 세 번째 큐비트에 X 게이트를 적용한다. 마지막으로, 우리는 세 번째 큐비트를 측정합니다. 명확성을 위해 조건에서 if 세 번째 클래식 비트(0)의 상태를 명시하기로 선택했음을 유의하십시오. 회로도에서 조건은 조건부로 설정된 클래식 비트 위의 원으로 표시됩니다. 실선 원은 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}

Output:

Output of the previous code cell

Switch문

switch 문은 일반 비트나 레지스터의 값에 따라 수행할 작업을 선택하는 데 사용됩니다. if 문과 비슷하지만, 분기 논리에 더 많은 경우를 지정할 수 있습니다. 아래 예제에서는 큐비트에 하다마르 게이트를 적용한 후 이를 측정합니다. 결과가 0이면 큐비트에 X 게이트를 적용하고, 결과가 1이면 Z 게이트를 적용한다. 그 결과 측정값은 100%의 확률로 1이 되어야 합니다.

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

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.switch(c0) as case:
    with case(0):
        circuit.x(q0)
    with case(1):
        circuit.z(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

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

Output:

Output of the previous code cell

위의 예제에서는 하나의 고전적 비트를 사용했기 때문에 가능한 경우가 두 가지뿐이었으므로, if-else 문을 사용해도 동일한 결과를 얻을 수 있었을 것입니다. switch 문은 주로 여러 비트로 구성된 일반 레지스터의 값에 따라 분기할 때 유용합니다. 다음 예제는 앞선 모든 조건이 충족되지 않을 경우 실행되는 기본 조건을 구성하는 방법을 보여줍니다. switch 문에서는 블록 중 오직 하나만 실행된다는 점에 유의하십시오. 폴스루는 발생하지 않습니다.

아래 예제에서는 두 큐비트에 하다마르 게이트를 적용한 후 이를 측정합니다. 결과가 00이거나 11인 경우, 세 번째 큐비트에 Z 게이트를 적용한다. 결과가 01이면 Y 게이트를 적용하십시오. 앞서 언급한 경우 중 어느 것도 해당되지 않는다면, X 게이트를 적용하십시오. 마지막으로, 세 번째 큐비트를 측정합니다.

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.switch(clbits) as case:
    with case(0b000, 0b011):
        circuit.z(q2)
    with case(0b001):
        circuit.y(q2)
    with case(case.DEFAULT):
        circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 267, '110': 249, '011': 265, '000': 243}

Output:

Output of the previous code cell

For 루프

A for 루프는 일련의 값을 순차적으로 처리하며, 각 반복마다 특정 연산을 수행하는 데 사용됩니다.

다음 예제에서는 for 루프를 사용하여 1개의 큐비트에 5개의 X 게이트를 적용한 다음, 그 상태를 측정합니다. 홀수 개의 X 게이트가 수행되기 때문에, 전체적인 효과는 큐비트를 0 상태에서 1 상태로 반전시키는 것입니다.

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

with circuit.for_loop(range(5)) as _:
    circuit.x(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

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

Output:

Output of the previous code cell

While 루프

while 루프는 특정 조건이 충족되는 동안 명령어를 반복하는 데 사용됩니다.

아래 예제에서는 두 큐비트에 하다마르 게이트를 적용한 후 이를 측정합니다. 그런 다음 측정 결과가 11인 동안 이 절차를 반복하는 while 루프를 생성합니다. 따라서 최종 측정값이 11이 되어서는 절대 안 되며, 나머지 가능성들은 대략 동일한 빈도로 나타날 것입니다.

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)

q0, q1 = qubits
c0, c1 = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.while_loop((clbits, 0b11)):
    circuit.h([q0, q1])
    circuit.measure(q0, c0)
    circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 334, '10': 368, '00': 322}

Output:

Output of the previous code cell

고전적 표현들

Qiskit의 고전 표현 모듈에는 qiskit.circuit.classical 회로 실행 중 고전 값에 대한 런타임 연산을 탐색적으로 표현한 내용이 포함되어 있습니다.

다음 예는 패리티 계산을 사용하여 동적 회로를 사용하여 n-쿼비트 GHZ 상태를 생성하는 방법을 보여줍니다. 먼저, 인접한 큐비트에 n/2n/2 벨 쌍을 생성합니다. 그런 다음 쌍 사이에 CNOT 게이트 층을 사용하여 이 쌍을 함께 붙입니다. 그런 다음 모든 이전 CNOT 게이트의 목표 큐비트를 측정하고 측정된 각 큐비트를 0\vert 0 \rangle 상태로 재설정합니다. 이전 모든 비트의 패리티가 홀수인 측정되지 않은 모든 사이트에 XX 을 적용합니다. 마지막으로, 측정된 큐비트에 CNOT 게이트를 적용하여 측정에서 손실된 얽힘을 다시 설정합니다.

패리티 계산에서, 생성된 표현식의 첫 번째 요소는 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)

Output:

Output of the previous code cell

Store

해당 식을 반복해서 사용할 예정이라면, 명령을 store 사용하여 그 결과를 저장할 수 있습니다. 작업이 자동으로 병렬화되어, 실행 시 코드의 효율성이 크게 향상됩니다.

예를 들어, (¬A[0])(¬A[1])(¬A[2])(\neg A[0]) \oplus (\neg A[1]) \oplus (\neg A[2]) \ldots 보다 B[0]B[1]B[2]B[0] \oplus B[1] \oplus B[2] \ldots (여기서 B=¬AB = \neg A)를 작성하는 것이 실행 시 더 자연스럽고 효율적이다. 전자는 식 내부에서 각 부정 연산을 순차적으로 평가하는 대신, XOR 연산 체인 전에 단일 병렬 단계로 부정 연산을 계산한다.

전체 예시:

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

qregs = QuantumRegister(4, "q")
creg = ClassicalRegister(3, "c")
# temp is a plain ClassicalRegister used as the store target
temp = ClassicalRegister(3, "temp")
qc = QuantumCircuit(qregs, creg, temp)

qc.h([0, 1, 2])
qc.measure([0, 1, 2], creg)

# Store bit-NOT of the full 3-bit register into temp
qc.store(temp, expr.bit_not(creg))

# Compute parity of temp using bit-indexed XOR
parity = expr.bit_xor(
    expr.bit_xor(expr.index(temp, 0), expr.index(temp, 1)),
    expr.index(temp, 2),
)

# Flip q3 if parity of ~creg is 1
with qc.if_test(parity):
    qc.x(3)

qc.measure([0, 1, 2], creg)

qc.draw("mpl")

Output:

Output of the previous code cell

다음 단계

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