Lab 4. Angle Encoding과 CNOT
실습 실행: Google Colab에서 바로 실행
학습 목표
- 고전 데이터를 양자 회로에 넣는 방법(Angle Encoding)을 이해한다
- 스케일링 없이 인코딩하면 주기 반복 문제가 생기는 것을 확인한다
- CNOT 게이트가 두 큐비트 사이에 상호작용을 만드는 것을 관찰한다
핵심 개념
Angle Encoding
데이터 값 x를 RY(x) 게이트의 각도로 사용한다. 출력은 ⟨Z⟩ = cos(x)이다.
문제는 cos()이 주기 함수라는 점이다. x=1과 x=1+2π는 같은 상태가 된다. 따라서 데이터를 [0, π] 범위로 스케일링해야 한다.
CNOT의 역할
CNOT 없이 RY(x₁) ⊗ RY(x₂)를 적용하면 두 큐비트는 독립이다. CNOT을 추가하면 확률분포가 재배치되면서 x₁과 x₂ 사이의 상호작용이 생긴다.
데이터 준비
홀수 수열(x₁)과 짝수 수열(x₂)을 사용한다. 범위가 1~100으로 넓어서 스케일링 문제를 직접 체감할 수 있다.
x1 = np.array([2*i + 1 for i in range(50)]) # 홀수: 1, 3, ..., 99
x2 = np.array([2*(i + 1) for i in range(50)]) # 짝수: 2, 4, ..., 100

Min-Max 스케일링
데이터를 [0, π] 범위로 변환하면, cos() 함수의 단조 감소 구간만 사용하게 된다.
시각화

해석
스케일링 후에는 x₁이 커질수록 ⟨Z⟩가 단조 감소한다. 주기 반복이 사라지고, 모든 데이터가 고유한 양자 상태로 매핑된다.
| 스케일링 | x₁=1 | x₁=99 | 패턴 |
|---|---|---|---|
| 없음 | 0.5403 | -0.0390 | 주기 반복, 구별 불가 |
| [0, π] | ~1.0 | ~-1.0 | 단조 감소, 구별 가능 |
scale_max를 2π로 바꾸면 주기 반복이 다시 나타난다. 이것은 학생 수정 구간에서 직접 확인할 수 있다.
회로 정의: CNOT 유무 비교
두 데이터(x₁, x₂)를 각각 큐비트 0, 1에 RY로 인코딩한다. CNOT이 없으면 두 큐비트는 독립이고, CNOT을 추가하면 상호작용이 생긴다.
회로 시각화
확률분포 비교
시각화

해석
CNOT 없이는 P(|00⟩) = P₀(0) × P₁(0)으로, 두 큐비트의 확률이 곱으로 분해된다. 독립적이다.
CNOT을 추가하면 확률이 재배치된다. 특정 상태의 확률이 올라가고 다른 상태는 내려간다. 이것이 "큐비트 간 상호작용"이며, IQP Embedding의 교차항(ZZ)과 같은 역할을 한다.
| CNOT 없음 | CNOT 있음 | |
|---|---|---|
| 큐비트 관계 | 독립 (곱으로 분해) | 상호작용 (재배치) |
| 표현력 | 1차원 × 1차원 | 2차원 결합 |
| QML 활용 | 불충분 | feature 간 관계 포착 |
과제 1: scale_max = 2π
학생 수정 구간에서 scale_max = 2 * np.pi로 변경하고 4-2를 재실행하라. 단조 감소가 유지되는가, 주기 반복이 다시 나타나는가?
# 학생 수정: scale_max = 2 * np.pi
# 4-2 재실행 후 그래프 관찰
과제 2: idx_sample 변경
idx_sample을 0(가장 작은 값)과 49(가장 큰 값)로 각각 실행하라. CNOT의 효과가 데이터 값에 따라 달라지는가?
# 학생 수정: idx_sample = 0 으로 변경 후 4-3 재실행
# 다시: idx_sample = 49 로 변경 후 4-3 재실행
# CNOT 없음과 있음의 차이가 달라지는지 관찰
과제 3: CNOT 방향 반전
encode_with_cnot에서 CNOT의 방향을 [0, 1]에서 [1, 0]으로 바꾸면 확률분포가 달라지는가?
@qml.qnode(dev2)
def encode_cnot_reversed(x1_val, x2_val):
qml.RY(x1_val, wires=0)
qml.RY(x2_val, wires=1)
qml.CNOT(wires=[1, 0]) # 방향 반전
return qml.probs(wires=[0, 1])
p_rev = encode_cnot_reversed(x1_sc, x2_sc)
print(f" CNOT [0→1]: {[f'{v:.3f}' for v in p_yes]}")
print(f" CNOT [1→0]: {[f'{v:.3f}' for v in p_rev]}")
control 큐비트와 target 큐비트의 역할이 바뀌면 확률분포도 달라진다. "어느 큐비트가 다른 큐비트를 제어하는가"가 회로 설계의 중요한 선택이다.