Korean
언어
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

참고

이 페이지는 tutorials/circuits/3_summary_of_quantum_operations.ipynb 에서 생성되었다.

양자 연산 요약

본 섹션에서는 Qiskit Terra에서 이용 가능한 여러 가지 연산들에 대하여 다룬다. 목록은 다음과 같다:

  • 단일 큐비트 양자 게이트

  • 다중 큐비트 양자 게이트

  • 측정

  • 초기화 (Reset)

  • 조건

  • 상태 초기화

또한 세 가지 종류의 시뮬레이터를 사용하는 방법도 설명한다.

  • unitary_simulator

  • qasm_simulator

  • statevector_simulator

[1]:
# Useful additional packages
import matplotlib.pyplot as plt
import numpy as np
from math import pi
[2]:
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile
from qiskit.tools.visualization import circuit_drawer
from qiskit.quantum_info import state_fidelity
from qiskit import BasicAer

backend = BasicAer.get_backend('unitary_simulator')

단일 큐비트 양자 상태

단일 큐비트 양자 상태는 다음과 같이 기술할 수 있다

\[\left|\psi\right\rangle = \alpha\left|0\right\rangle + \beta \left|1\right\rangle\]

여기서 \(\alpha\)\(\beta\) 는 복소수이다. 측정에서 큐비트가 \(\left|1\right\rangle\) 에 있을 확률은 \(|\alpha|^2\) 이고 \(\left|1\right\rangle\)\(|\beta|^ 2\). 벡터로서 이것은

\[\begin{split}\left|\psi\right\rangle = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}.\end{split}\]

확률의 보존으로 인해 \(|\alpha|^2+ |\beta|^2 = 1\) 이며 전역 위상이 감지되지 않기 때문에 \(\left|\psi\right\rangle := e^{i\delta} \left|\psi\right\rangle\) 단일 큐비트 양자 상태를 설명하기 위해 두 개의 실수만 필요하다.

\(0\leq\phi<2\pi\)\(0\leq\theta\leq\pi\) 때 다음과 같이 나타낼수 있다.

\[\left|\psi\right\rangle = \cos(\theta/2)\left|0\right\rangle + \sin(\theta/2)e^{i\phi}\left|1\right\rangle\]

여기서 \(0\leq\phi<2\pi\)\(0\leq\theta\leq\pi\) 이다. 물론 큐비트 상태 (\(\mathbb{C}^2\))와 단위 구 표면의 점 (\(\mathbb{R}^3\))은 일대일로 대응된다. 이를 큐비트 상태의 블로흐 구체 표현이라고 한다.

양자 게이트/연산은 일반적으로 행렬로 표현한다. 특히 큐비트에 작용하는 게이트는 \(2\times2\) 유니테리 행렬 \(U\) 로 표현 된다. 그리고 양자 게이트의 작용은 양자 상태를 나타내는 벡터에 게이트를 나타내는 행렬을 곱함으로써 알 수 있다.

\[\left|\psi'\right\rangle = U\left|\psi\right\rangle\]

일반적으로 유니테리 상태에서는 \(\left|0\right\rangle\) 을 위의 상태로 만들 수 있어야 한다. 따라서 \(U\) 는 다음과 같다.

\[\begin{split}U = \begin{pmatrix} \cos(\theta/2) & a \\ e^{i\phi}\sin(\theta/2) & b \end{pmatrix}\end{split}\]

여기서 임의로 주어진 \(0\leq\theta\leq\pi\)\(0\leq\phi<2\pi\) 에 대해 \(a\)\(b\)\(U^\dagger U=I\) 를 만족하는 복소수이다. 이는 세가지 제약 조건을 제공하며 이를 만족하는 \(a\)\(b\)\(0\leq\lambda<2\pi\) 를 이용하여 \(a\rightarrow -e^{i\lambda}\sin(\theta/2)\)\(b\rightarrow e^{i\lambda+i\phi}\cos(\theta/2)\) 로 표현할 수 있다. 따라서

\[\begin{split}U(\theta, \phi, \lambda) = \begin{pmatrix} \cos\left(\frac{\theta}{2}\right) & -e^{i\lambda}\sin\left(\frac{\theta}{2}\right) \\ e^{i\phi}\sin\left(\frac{\theta}{2}\right) & e^{i(\phi+\lambda)}\cos\left(\frac{\theta}{2}\right) \end{pmatrix}\end{split}\]

이는 단일 큐비트에 작용되는 유니터리의 가장 일반적인 형태이다.

단일 큐비트 게이트

사용할 수 있는 단일 큐비트 게이트는 다음과 같다: - U 게이트 - P 게이트 - Identity 게이트 - Pauli 게이트 - Clifford 게이트 - \(C3\) 게이트 - 표준 회전(Standard rotation) 게이트

우리는 이러한 유니터리 행렬 계산이 가능하도록 해 주는 백엔드: unitary_simulator 를 제공한다.

[3]:
q = QuantumRegister(1)

U 게이트

Qiskit에서는 다음과 같은 행렬 형식을 가진 \(u\) 게이트를 사용하여 일반 유니테리를 사용할 수 있다.

\[\begin{split}U(\theta, \phi, \lambda) = \begin{pmatrix} \cos\left(\frac{\theta}{2}\right) & -e^{i\lambda}\sin\left(\frac{\theta}{2}\right) \\ e^{i\phi}\sin\left(\frac{\theta}{2}\right) & e^{i(\phi+\lambda)}\cos\left(\frac{\theta}{2}\right) \end{pmatrix}\end{split}\]
[4]:
qc = QuantumCircuit(q)
qc.u(pi/2,pi/4,pi/8,q)
qc.draw()
[4]:
    ┌────────────────┐
q0: ┤ U(π/2,π/4,π/8) ├
    └────────────────┘
[5]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[5]:
array([[ 0.707+0.j   , -0.653-0.271j],
       [ 0.5  +0.5j  ,  0.271+0.653j]])

U 게이트 사용중단에 대한 노트

QuantumCircuit의 메서드들 중 :math:》u1》, :math:》u2》 및 :math:》u3》 은 이제 더 이상 사용되지 않는다. 대신에 다음 메서드들을 사용한다.

  • \(u1(\lambda) = p(\lambda) = u(0, 0, \lambda)\)

  • \(u2(\phi, \lambda) = u(\frac{\pi}{2}, \phi, \lambda) = p(\frac{\pi}{2} + \phi) \cdot sx \cdot p(\frac{\pi}{2} - \lambda)\)

  • \(u3(\theta, \phi, \lambda) = u(\theta, \phi, \lambda) = p(\phi + \pi) \cdot sx \cdot p(\theta + \pi) \cdot sx \cdot p(\lambda)\)

# qc.u1(lambda) is now:
qc.p(lambda)

# qc.u2(phi, lambda) is now:
qc.u(pi/2, phi, lambda)

# qc.u3(theta, phi, lambda) is now:
qc.u(theta, phi, lambda)

P 게이트

게이트 \(p(\lambda)= u(0, 0, \lambda)\) 는 다음과 같은 행렬로 표현된다.

\[\begin{split}p(\lambda) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \lambda} \end{pmatrix},\end{split}\]

이는 양자 위상을 만들어 주므로 유용하다.

[13]:
qc = QuantumCircuit(q)
qc.p(pi/2,q)
qc.draw()
[13]:
    ┌────────┐
q0: ┤ P(π/2) ├
    └────────┘
[14]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[14]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

항등(identity) 게이트

항등 게이트는 \(Id = p(0)\) 이다.

[15]:
qc = QuantumCircuit(q)
qc.id(q)
qc.draw()
[15]:
    ┌───┐
q0: ┤ I ├
    └───┘
[16]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[16]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j]])

파울리(Pauli) 게이트

\(X\): 비트 플립 게이트

비트 플립 게이트 \(X\) 는 다음과 같이 정의된다.

\[\begin{split}X = \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix}= u(\pi,0,\pi)\end{split}\]
[17]:
qc = QuantumCircuit(q)
qc.x(q)
qc.draw()
[17]:
    ┌───┐
q0: ┤ X ├
    └───┘
[18]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[18]:
array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]])

\(Y\): 비트와 위상 플립 게이트

\(Y\) 게이트는 다음과 같이 정의된다.

\[\begin{split}Y = \begin{pmatrix} 0 & -i\\ i & 0 \end{pmatrix}=u(\pi,\pi/2,\pi/2)\end{split}\]
[19]:
qc = QuantumCircuit(q)
qc.y(q)
qc.draw()
[19]:
    ┌───┐
q0: ┤ Y ├
    └───┘
[20]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[20]:
array([[ 0.+0.j, -0.-1.j],
       [ 0.+1.j,  0.+0.j]])

\(Z\): 위상 플립 게이트

위상 플립 게이트 \(Z\) 는 다음과 같이 정의된다.

\[\begin{split}Z = \begin{pmatrix} 1 & 0\\ 0 & -1 \end{pmatrix}=p(\pi)\end{split}\]
[21]:
qc = QuantumCircuit(q)
qc.z(q)
qc.draw()
[21]:
    ┌───┐
q0: ┤ Z ├
    └───┘
[23]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[23]:
array([[ 1.+0.j,  0.+0.j],
       [ 0.+0.j, -1.+0.j]])

클리포드 (Clifford) 게이트

하다마드(Hadamard) 게이트

\[\begin{split}H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1\\ 1 & -1 \end{pmatrix}= u(\pi/2,0,\pi)\end{split}\]
[22]:
qc = QuantumCircuit(q)
qc.h(q)
qc.draw()
[22]:
    ┌───┐
q0: ┤ H ├
    └───┘
[23]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[23]:
array([[ 0.707+0.j,  0.707-0.j],
       [ 0.707+0.j, -0.707+0.j]])

\(S\) (or, \(\sqrt{Z}\) 위상) 게이트

\[\begin{split}S = \begin{pmatrix} 1 & 0\\ 0 & i \end{pmatrix}= p(\pi/2)\end{split}\]
[24]:
qc = QuantumCircuit(q)
qc.s(q)
qc.draw()
[24]:
    ┌───┐
q0: ┤ S ├
    └───┘
[29]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[29]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

\(S^{\dagger}\) (또는 \(\sqrt{Z}\) 위상의 켤레) 게이트

\[\begin{split}S^{\dagger} = \begin{pmatrix} 1 & 0\\ 0 & -i \end{pmatrix}= p(-\pi/2)\end{split}\]
[25]:
qc = QuantumCircuit(q)
qc.sdg(q)
qc.draw()
[25]:
    ┌─────┐
q0: ┤ Sdg ├
    └─────┘
[26]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[26]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.-1.j]])

\(C3\) 게이트

\(T\) (혹은, \(\sqrt{S}\) 위상) 게이트

\[\begin{split}T = \begin{pmatrix} 1 & 0\\ 0 & e^{i \pi/4} \end{pmatrix}= p(\pi/4)\end{split}\]
[27]:
qc = QuantumCircuit(q)
qc.t(q)
qc.draw()
[27]:
    ┌───┐
q0: ┤ T ├
    └───┘
[28]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[28]:
array([[1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707+0.707j]])

\(T^{\dagger}\) (혹은, \(\sqrt{S}\) 위상의 켤레) 게이트

\[\begin{split}T^{\dagger} = \begin{pmatrix} 1 & 0\\ 0 & e^{-i \pi/4} \end{pmatrix}= p(-\pi/4)\end{split}\]
[29]:
qc = QuantumCircuit(q)
qc.tdg(q)
qc.draw()
[29]:
    ┌─────┐
q0: ┤ Tdg ├
    └─────┘
[30]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[30]:
array([[1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707-0.707j]])

표준 회전

표준 회전 게이트들은 파울리 \(P=\{X,Y,Z\}\) 에 대응하는 블로흐 벡터 축을 기준으로한 회전을 정의하는 게이트다. 다음과 같이 정의되는데,

\[R_P(\theta) = \exp(-i \theta P/2) = \cos(\theta/2)I -i \sin(\theta/2)P\]

X축에 관한 회전

\[\begin{split}R_x(\theta) = \begin{pmatrix} \cos(\theta/2) & -i\sin(\theta/2)\\ -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} = u(\theta, -\pi/2,\pi/2)\end{split}\]
[31]:
qc = QuantumCircuit(q)
qc.rx(pi/2,q)
qc.draw()
[31]:
    ┌─────────┐
q0: ┤ Rx(π/2) ├
    └─────────┘
[32]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[32]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   -0.707j,  0.707+0.j   ]])

Y축에 관한 회전

\[\begin{split}R_y(\theta) = \begin{pmatrix} \cos(\theta/2) & - \sin(\theta/2)\\ \sin(\theta/2) & \cos(\theta/2). \end{pmatrix} =u(\theta,0,0)\end{split}\]
[33]:
qc = QuantumCircuit(q)
qc.ry(pi/2,q)
qc.draw()
[33]:
    ┌─────────┐
q0: ┤ Ry(π/2) ├
    └─────────┘
[34]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[34]:
array([[ 0.707+0.j, -0.707+0.j],
       [ 0.707+0.j,  0.707+0.j]])

Z축에 관한 회전

\[\begin{split}R_z(\phi) = \begin{pmatrix} e^{-i \phi/2} & 0 \\ 0 & e^{i \phi/2} \end{pmatrix}\equiv p(\phi)\end{split}\]

\(p\) 과 전역 위상 \(e^{-i \phi/2}\) 의 차이로 인해 등가식을 사용했음을 참고한다.

[35]:
qc = QuantumCircuit(q)
qc.rz(pi/2,q)
qc.draw()
[35]:
    ┌─────────┐
q0: ┤ Rz(π/2) ├
    └─────────┘
[36]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[36]:
array([[0.707-0.707j, 0.   +0.j   ],
       [0.   +0.j   , 0.707+0.707j]])

이는 전역 위상 (Global Phase) 값만 다를 뿐이라는 사실을 주목하자.

다중 큐비트 게이트

수학적 예비

The space of a quantum computer grows exponentially with the number of qubits. For \(n\) qubits the complex vector space has dimension \(d=2^n\). To describe states of a multi-qubit system, the tensor product is used to 《glue together》 operators and basis vectors.

Let’s start by considering a 2-qubit system. Given two operators \(A\) and \(B\) that each act on one qubit, the joint operator \(A \otimes B\) acting on two qubits is

\[\begin{split}\begin{equation} A\otimes B = \begin{pmatrix} A_{00} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} & A_{01} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} \\ A_{10} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} & A_{11} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} \end{pmatrix}, \end{equation}\end{split}\]

여기서 \(A_{jk}\)\(B_{lm}\) 는 각각 \(A\)\(B\) 의 행렬 성분이다.

유사하게 두 큐비트 시스템의 기저 벡터들은 단일 큐비트의 기저 벡터들을 텐서 곱하여 형성된다.

\[\begin{split}\begin{equation}\begin{split} \left|{00}\right\rangle &= \begin{pmatrix} 1 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ 0 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 1 \\ 0 \\ 0 \\0 \end{pmatrix}~~~\left|{01}\right\rangle = \begin{pmatrix} 1 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 0 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} = \begin{pmatrix}0 \\ 1 \\ 0 \\ 0 \end{pmatrix}\end{split} \end{equation}\end{split}\]
\[\begin{split}\begin{equation}\begin{split}\left|{10}\right\rangle = \begin{pmatrix} 0\begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ 1\begin{pmatrix} 1 \\ 0 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}~~~ \left|{11}\right\rangle = \begin{pmatrix} 0 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 1\begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 0 \\1 \end{pmatrix}\end{split} \end{equation}.\end{split}\]

Note we’ve introduced a shorthand for the tensor product of basis vectors, wherein \(\left|0\right\rangle \otimes \left|0\right\rangle\) is written as \(\left|00\right\rangle\). The state of an \(n\)-qubit system can be described using the \(n\)-fold tensor product of single-qubit basis vectors. Notice that the basis vectors for a 2-qubit system are 4-dimensional; in general, the basis vectors of an \(n\)-qubit system are \(2^{n}\)-dimensional, as noted earlier.

Qiskit에 사용되는 기저 벡터의 순서

물리 커뮤니티 내에서, 다중 큐비트 시스템의 큐비트는 일반적으로 텐서 곱의 가장 왼쪽에 있는 첫 번째 큐비트와 가장 오른쪽에 있는 마지막 큐비트로 순서를 결정한다. 예를 들어, 첫 번째 큐비트가 상태 \(\left|0\right\rangle\) 이고 두 번째 큐비트가 \(\left|1\right\rangle\) 상태인 경우 결합 상태는 \(\left|01\right\rangle\) 로 정하는 것이 일반적이다. 하지만 Qiskit은 큐비트의 정렬 순서를 다르게 사용한다. 큐비트는 왼쪽의 최상위 비트(MSB)에서 오른쪽(리틀 엔디안)의 최하위 비트(LSB)로 표시된다. 이 방식은 기존 컴퓨터의 비트열 표현과 유사하기 때문에 측정 후 비트열을 정수로 쉽게 변환할 수 있다. 따라서 방금 주어진 예의 연합 상태는 \(\left|10\right\rangle\) 로 표시된다. 아주 중요하게, 다중 큐비트 상태 표현의 이런 차이점은 아래에서 살펴볼 바와 같이 Qiskit 에서 다중 큐비트 게이트가 표현되는 방식에 영향을 미친다.

Qiskit에서 사용하는 표현은 기저 벡터를 표현하는 숫자가 증가하는 순으로 숫자를 메긴다. 예를 들어 두 큐비트 시스템의 기저 벡터들은 \(\left|00\right\rangle\), \(\left|01\right\rangle\), \(\left|10\right\rangle\), and \(\left|11\right\rangle\) 순으로 배열된다. 이때 기저 벡터에 대응하는 비트열을 살펴보면 각각 0,1,2,3이 인코딩 되어 있다.

큐비트에 적용되는 조절 연산들

일반적인 다중 큐비트 게이트는 한 큐비트에 가하는 게이트나 다른 큐비트에 조건부로 작용하는 게이트나 그 응용을 모두 포함한다. 예를들어 첫번째 큐비트이 \(\left|0\right\rangle\) 일 때 두번째 큐비트가 플립되는 게이트를 고려하자. 이러한 게이트는 제어 게이트 (controlled gates)로 알려져 있다. 표준 다중 큐비트 게이트는 두 큐비트 게이트와 세 큐비트 게이트로 구성되어 있는데 두 비트 게이트에는 -제어 파울리 게이트 (controlled Pauli gates) - 제어 하다마드 게이트 (controlled Hadamard gate) - 제어 회전 게이트 (controlled rotation gates) - 제어 위상 게이트 (controlled phase gate) - 제어 u3게이트 (controlled u3 gate) - 교환 게이트 (swap gate)가 있으며

세 큐비트 게이트에는 - Toffoli gate - Fredkin gate가 있다.

이중 큐비트 게이트

대부분의 두 큐비트 게이트들은 다른 큐비트에 의해 조절되는 유형이다. (SWAP 게이트는 예외) 일반적으로 다른 큐비트에 의해 조절되는 두 큐비트 게이트 \(C_{U}\) 는 첫번째 큐비트의 상태가 \(\left|1\right\rangle\) 일 때 단일 큐비트 유니터리 \(U\) 를 두번째 큐비트에 가하는 식이다. 유니터리 \(U\) 가 다음 행렬이라고 가정하자.

\[\begin{split}U = \begin{pmatrix} u_{00} & u_{01} \\ u_{10} & u_{11}\end{pmatrix}.\end{split}\]

We can work out the action of \(C_{U}\) as follows. Recall that the basis vectors for a two-qubit system are ordered as \(\left|00\right\rangle, \left|01\right\rangle, \left|10\right\rangle, \left|11\right\rangle\). Suppose the control qubit is qubit 0 (which, according to Qiskit’s convention, is one the right-hand side of the tensor product). If the control qubit is in \(\left|1\right\rangle\), \(U\) should be applied to the target (qubit 1, on the left-hand side of the tensor product). Therefore, under the action of \(C_{U}\), the basis vectors are transformed according to

\[\begin{split}\begin{align*} C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{U\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{U\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ \end{align*}.\end{split}\]

\(C_{U}\) 를 행렬로 나타내면 다음과 같다.

\[\begin{split}\begin{equation} C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & u_{00} & 0 & u_{01} \\ 0 & 0 & 1 & 0 \\ 0 & u_{10} &0 & u_{11} \end{pmatrix}. \end{equation}\end{split}\]

이러한 행렬 성분을 알아내려면,

\[C_{(jk), (lm)} = \left(\underset{\text{qubit}~1}{\left\langle j \right|} \otimes \underset{\text{qubit}~0}{\left\langle k \right|}\right) C_{U} \left(\underset{\text{qubit}~1}{\left| l \right\rangle} \otimes \underset{\text{qubit}~0}{\left| m \right\rangle}\right),\]

위에 주어진 \(C_{U}\) 의 작용을 먼저 계산하고 내적을 계산한다.

아래 예에서 볼 수 있듯이 이 연산은 Qiskit에서 cU(q[0],q[1]) 로 구현되어 있다.

만약 큐비트 1이 제어 큐비트 그리고 큐비트 0이 표적 큐비트 이라면, 기저 벡터는 다음과 같이 변환된다.

\[\begin{split}\begin{align*} C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{U\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{U\left|1\right\rangle}\\ \end{align*},\end{split}\]

이는 \(C_{U}\) 의 행렬 형태가 다음과 같음을 의미한다.

\[\begin{split}\begin{equation} C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & u_{00} & u_{01} \\ 0 & 0 & u_{10} & u_{11} \end{pmatrix}. \end{equation}\end{split}\]
[37]:
q = QuantumRegister(2)

제어 파울리 게이트(Controlled Pauli Gate)

Controlled-X (또는, Controlled-NOT) 게이트

The Controlled-NOT gate flips the target qubit when the control qubit is in the state \(\left|1\right\rangle\). If we take the MSB as the control qubit (e.g. cx(q[1],q[0])), then the matrix would look like

\[\begin{split}C_X = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{pmatrix}.\end{split}\]

However, when the LSB is the control qubit, (e.g. cx(q[0],q[1])), this gate is equivalent to the following matrix:

\[\begin{split}C_X = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0 \end{pmatrix}.\end{split}\]
[38]:
qc = QuantumCircuit(q)
qc.cx(q[0],q[1])
qc.draw()
[38]:
q60_0: ──■──
       ┌─┴─┐
q60_1: ┤ X ├
       └───┘
[39]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[39]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]])

제어 \(Y\) 게이트 (Controlled Y Gate)

조절 큐비트이 MSB이면 \(Y\) 게이트를 목표 큐비트에 적용한다.

\[\begin{split}C_Y = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & -i\\ 0 & 0 & i & 0 \end{pmatrix},\end{split}\]

만약 조절 큐비트이 LSB이면 다음을 적용한다.

\[\begin{split}C_Y = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & -i\\ 0 & 0 & 1 & 0\\ 0 & i & 0 & 0 \end{pmatrix}.\end{split}\]
[40]:
qc = QuantumCircuit(q)
qc.cy(q[0],q[1])
qc.draw()
[40]:
q60_0: ──■──
       ┌─┴─┐
q60_1: ┤ Y ├
       └───┘
[41]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[41]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j]])

Controlled \(Z\) (위상 제어) 게이트

비슷하게, 위상 반전 게이트는 표적 큐비트의 상태가 \(\left|1\right\rangle\) 일 때 제어 큐비트의 위상을 뒤집어 준다. 제어 큐비트가 MSB인 경우나 LSB인 경우나 상관없이 행렬은 같은 모양이다.

\[\begin{split}C_Z = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & -1 \end{pmatrix}\end{split}\]
[42]:
qc = QuantumCircuit(q)
qc.cz(q[0],q[1])
qc.draw()
[42]:
q60_0: ─■─
        │
q60_1: ─■─
          
[43]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[43]:
array([[ 1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j]])

조절된 Hadamard 게이트

만약 제어 큐비트가 \(\left|1\right\rangle\) 이면 \(H\) 를 타겟 큐비트에 적용한다. 아래는 제어 큐비트가 LSB 큐비트인 경우이다.

\[\begin{split}C_H = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & \frac{1}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}}\\ 0 & 0 & 1 & 0\\ 0 & \frac{1}{\sqrt{2}} & 0& -\frac{1}{\sqrt{2}} \end{pmatrix}\end{split}\]
[44]:
qc = QuantumCircuit(q)
qc.ch(q[0],q[1])
qc.draw()
[44]:
q60_0: ──■──
       ┌─┴─┐
q60_1: ┤ H ├
       └───┘
[45]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[45]:
array([[ 1.   +0.j,  0.   +0.j,  0.   +0.j,  0.   +0.j],
       [ 0.   +0.j,  0.707+0.j,  0.   +0.j,  0.707-0.j],
       [ 0.   +0.j,  0.   +0.j,  1.   -0.j,  0.   +0.j],
       [ 0.   +0.j,  0.707+0.j,  0.   +0.j, -0.707+0.j]])

회전 제어 게이트 (Controlled rotation gate)

Z-축을 기준으로 한 회전 제어

만약 조절 큐비트 (여기서는 LSB)가 :math:`left|1rightrangle`인 경우 z-축을 중심으로 회전 변환을 실행한다.

\[\begin{split}C_{Rz}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & e^{-i\lambda/2} & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & e^{i\lambda/2} \end{pmatrix}\end{split}\]
[46]:
qc = QuantumCircuit(q)
qc.crz(pi/2,q[0],q[1])
qc.draw()
[46]:
q60_0: ─────■─────
       ┌────┴────┐
q60_1: ┤ Rz(π/2) ├
       └─────────┘
[47]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[47]:
array([[1.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707-0.707j, 0.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.   +0.j   , 1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.707+0.707j]])

위상 제어 회전

만약 두 큐비트가 모두 \(\left|11\right\rangle\) 상태에 있으면 위상 회전을 실시한다. 이 경우 조절 큐비트가 MSB나 LSB에 상관없이 행렬은 같은 형태이다.

\[\begin{split}C_{p}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & e^{i\lambda} \end{pmatrix}\end{split}\]
[48]:
qc = QuantumCircuit(q)
qc.cp(pi/2,q[0], q[1])
qc.draw()
[48]:
q60_0: ─■───────
        │P(π/2)
q60_1: ─■───────
                
[49]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[49]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j]])

제어 \(u\) 회전

제어 큐비트(여기서는 LSB)가 \(\left|1\right\rangle\) 인 경우 대상 큐비트에 controlled-\(u\) 회전을 수행한다.

\[\begin{split}C_{u}(\theta, \phi, \lambda) \equiv \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & e^{-i(\phi+\lambda)/2}\cos(\theta/2) & 0 & -e^{-i(\phi-\lambda)/2}\sin(\theta/2)\\ 0 & 0 & 1 & 0\\ 0 & e^{i(\phi-\lambda)/2}\sin(\theta/2) & 0 & e^{i(\phi+\lambda)/2}\cos(\theta/2) \end{pmatrix}.\end{split}\]
[53]:
qc = QuantumCircuit(q)
qc.cu(pi/2, pi/2, pi/2, 0, q[0], q[1])
qc.draw()
[53]:
q60_0: ─────────■──────────
       ┌────────┴─────────┐
q60_1: ┤ U(π/2,π/2,π/2,0) ├
       └──────────────────┘
[54]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[54]:
array([[ 1.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ],
       [ 0.   +0.j   ,  0.707+0.j   ,  0.   +0.j   ,  0.   -0.707j],
       [ 0.   +0.j   ,  0.   +0.j   ,  1.   -0.j   ,  0.   +0.j   ],
       [ 0.   +0.j   ,  0.   +0.707j,  0.   +0.j   , -0.707+0.j   ]])

교환 게이트(SWAP gate)

SWAP 게이트는 두 큐비트의 상태를 서로 바꿔 준다. 이는 기저 벡터를 다음과 같이 변환한다.

\[\left|00\right\rangle \rightarrow \left|00\right\rangle~,~\left|01\right\rangle \rightarrow \left|10\right\rangle~,~\left|10\right\rangle \rightarrow \left|01\right\rangle~,~\left|11\right\rangle \rightarrow \left|11\right\rangle,\]

이것은 다음의 행렬 표현식을 가진다.

\[\begin{split}\mathrm{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}.\end{split}\]
[55]:
qc = QuantumCircuit(q)
qc.swap(q[0], q[1])
qc.draw()
[55]:
q60_0: ─X─
        │
q60_1: ─X─
          
[56]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[56]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])

삼중 큐비트 게이트

일반적으로 사용되는 두 가지 삼중 큐비트 게이트가 있다. 세 큐비트의 경우 기저 벡터는 다음과 같이 정렬된다.

\[\left|000\right\rangle, \left|001\right\rangle, \left|010\right\rangle, \left|011\right\rangle, \left|100\right\rangle, \left|101\right\rangle, \left|110\right\rangle, \left|111\right\rangle,\]

비트열 처럼 정수 \(0,1,2,\cdots, 7\) 를 나타낸다. 다시한번 말하지만 Qiskit은 첫번째 큐비트를 가장 오른쪽에 세번째 큐비트를 가장 왼쪽에 위치하는 표현을 사용한다.

\[\left|abc\right\rangle : \underset{\text{qubit 2}}{\left|a\right\rangle}\otimes \underset{\text{qubit 1}}{\left|b\right\rangle}\otimes \underset{\text{qubit 0}}{\left|c\right\rangle}.\]

토폴리(Toffoli) 게이트 (\(ccx\) 게이트)

토폴리 게이트 는 첫번째와 두번째 큐비트(LSB)이 모두 \(\left|1\right\rangle\) 일 때 세번째 큐비트을 플립한다.

\[\left|abc\right\rangle \rightarrow \left|bc\oplus a\right\rangle \otimes \left|b\right\rangle \otimes \left|c\right\rangle.\]

행렬식으로 표현한 토폴리 게이트는 다음과 같다.

\[\begin{split}C_{CX} = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \end{pmatrix}.\end{split}\]
[57]:
q = QuantumRegister(3)
[58]:
qc = QuantumCircuit(q)
qc.ccx(q[0], q[1], q[2])
qc.draw()
[58]:
q105_0: ──■──
          │
q105_1: ──■──
        ┌─┴─┐
q105_2: ┤ X ├
        └───┘
[59]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[59]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])

교환 제어 게이트(프레드킨 게이트)(Controlled swap gate, Fredkin gate)

프레드킨 게이트 혹은 교환 제어 게이트(controlled swap gate) 는 첫번째(LSB) 큐비트이 \(\left|1\right\rangle\) 일 때 두번째와 세번째 큐비트를 교환한다.

\[\left|abc\right\rangle \rightarrow \begin{cases} \left|bac\right\rangle~~\text{if}~c=1 \cr \left|abc\right\rangle~~\text{if}~c=0 \end{cases}.\]

프레드킨 게이트를 행렬로 표현하면 다음과 같다.

\[\begin{split}C_{\mathrm{SWAP}} = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{pmatrix}.\end{split}\]
[60]:
qc = QuantumCircuit(q)
qc.cswap(q[0], q[1], q[2])
qc.draw()
[60]:
q105_0: ─■─
         │
q105_1: ─X─
         │
q105_2: ─X─
           
[61]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[61]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j]])

비 유니테리 연산들 (Non-unitary operations)

양자 회로에서 사용가능한 모든 유니터리 연산들을 살펴보았으니 이제 유니터리가 아닌 연산들을 살펴보도록 하자. 유니터리가 아닌 연산에는 측정, 큐비트의 reset, classical conditional 연산이 있다.

[62]:
q = QuantumRegister(1)
c = ClassicalRegister(1)

측정

We don’t have access to all the information when we make a measurement in a quantum computer. The quantum state is projected onto the standard basis. Below are two examples showing a circuit that is prepared in a basis state and the quantum computer prepared in a superposition state.

[63]:
qc = QuantumCircuit(q, c)
qc.measure(q, c)
qc.draw()
[63]:
      ┌─┐
q124: ┤M├
      └╥┘
c0: 1/═╩═
       0 
[64]:
backend = BasicAer.get_backend('qasm_simulator')
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[64]:
{'0': 1024}

이 시뮬레이터는 100퍼센트 확률로 고전적인 레지스터의 값이 0 임을 예측한다.

[65]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
qc.draw()
[65]:
      ┌───┐┌─┐
q124: ┤ H ├┤M├
      └───┘└╥┘
c0: 1/══════╩═
            0 
[66]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[66]:
{'1': 532, '0': 492}

이 시뮬레이터는 50퍼센트 확률로 고전적인 레지스터의 값이 0이거나 1임을 예측한다.

초기화 (Reset)

계산하는 동안 큐비트들을 \(\left|0\right\rangle\) 상태로 재설정 하는 것도 가능하다. 재설정 은 가역연산이 아니므로 게이트 연산이 아님을 주목하자.

[67]:
qc = QuantumCircuit(q, c)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[67]:
           ┌─┐
q124: ─|0>─┤M├
           └╥┘
c0: 1/══════╩═
            0 
[68]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[68]:
{'0': 1024}
[69]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[69]:
      ┌───┐     ┌─┐
q124: ┤ H ├─|0>─┤M├
      └───┘     └╥┘
c0: 1/═══════════╩═
                 0 
[70]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[70]:
{'0': 1024}

여기서 우리는 시뮬레이터가 두 회로 모두 결과값이 0 상태임을 100 퍼센트의 확률로 예측함을 알 수 있다.

조건 연산자

고전적인 레지스터의 상태에 따라 연산을 하는 것도 가능하다.

[71]:
qc = QuantumCircuit(q, c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[71]:
       ┌───┐ ┌─┐
q124: ─┤ X ├─┤M├
       └─╥─┘ └╥┘
      ┌──╨──┐ ║
c0: 1/╡ 0x0 ╞═╩═
      └─────┘ 0 

여기서 고전 비트의 값은 항상 0이라서 큐비트의 상태는 항상 플립된다.

[72]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[72]:
{'1': 1024}
[73]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q,c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[73]:
      ┌───┐┌─┐ ┌───┐ ┌─┐
q124: ┤ H ├┤M├─┤ X ├─┤M├
      └───┘└╥┘ └─╥─┘ └╥┘
            ║ ┌──╨──┐ ║
c0: 1/══════╩═╡ 0x0 ╞═╩═
            0 └─────┘ 0 
[74]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[74]:
{'1': 1024}

여기서 첫번째 측정에 따른 고전 비트는 무작위이지만 조건 연산의 결과 상태는 결정적으로 \(\left|1\right\rangle\) 이다.

임의 초기화

큐비트 레지스터를 임의의 상태로 초기화 하고 싶다면 어떻게 하면 될까? \(n\) 큐비트의 임의의 상태는 각 진폭을 절대값 해서 제곱한 값을 모두 더했을 때 1이 되는 \(2^n\) 개의 진폭을 나타내는 숫자로 구성된 벡터로 표현할 수 있다. 예를 들어, 다음의 세 큐비트 상태는 이렇게 준비될 수 있다.

\[\left|\psi\right\rangle = \frac{i}{4}\left|000\right\rangle + \frac{1}{\sqrt{8}}\left|001\right\rangle + \frac{1+i}{4}\left|010\right\rangle + \frac{1+2i}{\sqrt{8}}\left|101\right\rangle + \frac{1}{4}\left|110\right\rangle\]
[79]:
# Initializing a three-qubit quantum state
import math
desired_vector = [
    1 / math.sqrt(16) * complex(0, 1),
    1 / math.sqrt(8) * complex(1, 0),
    1 / math.sqrt(16) * complex(1, 1),
    0,
    0,
    1 / math.sqrt(8) * complex(1, 2),
    1 / math.sqrt(16) * complex(1, 0),
    0]


q = QuantumRegister(3)

qc = QuantumCircuit(q)

qc.initialize(desired_vector, [q[0],q[1],q[2]])
qc.draw()
[79]:
        ┌───────────────────────────────────────────────────────────────────┐
q233_0: ┤0                                                                  ├
        │                                                                   │
q233_1: ┤1 Initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├
        │                                                                   │
q233_2: ┤2                                                                  ├
        └───────────────────────────────────────────────────────────────────┘
[80]:
backend = BasicAer.get_backend('statevector_simulator')
job = backend.run(transpile(qc, backend))
qc_state = job.result().get_statevector(qc)
qc_state
[80]:
array([1.52655666e-16+2.50000000e-01j, 3.53553391e-01+5.55111512e-17j,
       2.50000000e-01+2.50000000e-01j, 0.00000000e+00+0.00000000e+00j,
       0.00000000e+00+0.00000000e+00j, 3.53553391e-01+7.07106781e-01j,
       2.50000000e-01-9.02056208e-17j, 0.00000000e+00+0.00000000e+00j])

Fidelity 는 두 상태가 같은지 확인하는데 유용하다. 두 pure 양자 상태 \(\left|\psi_1\right\rangle\)\(\left|\psi_2\right\rangle\) 의 fidelity는 다음과 같다.

\[F\left(\left|\psi_1\right\rangle,\left|\psi_2\right\rangle\right) = \left|\left\langle\psi_1\middle|\psi_2\right\rangle\right|^2.\]

두 상태가 동일할 때만 fidelity 값은 \(1\) 이 된다.

[81]:
state_fidelity(desired_vector,qc_state)
[81]:
1.0000000000000004

자세한 내용:

어떻게 원하는 상태가 준비될 수 있을까? 이를 수행할 수 있는 다양한 방법들이 있지만 Qiskit에서는 Shende et al이 제안한 방법 를 사용한다. 이 논문의 아이디어는 양자 레지스터가 애초에 원하는 상태로 시작했다고 가정한 다음 \(\left|00..0\right\rangle\) 상태로 변환하는 회로를 구성하는 것이다. 그리고 이 회로의 역변환을 하여 초기화 회로를 얻는다.

우리는 임의의 양자 상태를 계산가능한 기저(computational basis)의 영 상태(zero state)로 만들고자 큐비트을 얽힘을 푸는 과정을 반복한다. 임의의 단일 큐비트 상태 \(\left|\rho\right\rangle\) 는 Z축을 중심으로 회전하는 \(\phi\)-자유도 회전과 Y축을 중심으로 회전하는 \(\theta\)-자유도 회전을 연이어 수행하여 상태 \(\left|0\right\rangle\) 로 변환할 수 있다.

\[R_y(-\theta)R_z(-\phi)\left|\rho\right\rangle = re^{it}\left|0\right\rangle\]

우리가 지금 다루고 있는 큐비트은 한 개가 아닌 \(n\) 개이므로 상태 벡터에서 가정 덜 중요한 비트(LSB)를 분리해 내고자 다음과 같이 성분 별로 묶어 분해 한다.

\[\begin{split}\begin{align*} \left|\psi\right\rangle =& \alpha_{0_0}\left|00..00\right\rangle + \alpha_{0_1}\left|00..01\right\rangle + \alpha_{1_0}\left|00..10\right\rangle + \alpha_{1_1}\left|00..11\right\rangle + ... \\&+ \alpha_{(2^{n-1}-1)_0}\left|11..10\right\rangle + \alpha_{(2^{n-1}-1)_1}\left|11..11\right\rangle \\ =& \left|00..0\right\rangle (\alpha_{0_0}\left|0\right\rangle + \alpha_{0_1}\left|1\right\rangle) + \left|00..1\right\rangle (\alpha_{1_0}\left|0\right\rangle + \alpha_{1_1}\left|1\right\rangle) + ... \\&+ \left|11..1\right\rangle (\alpha_{(2^{n-1}-1)_0}(\left|0\right\rangle + \alpha_{(2^{n-1}-1)_1}\left|1\right\rangle) \\ =& \left|00..0\right\rangle\left|\rho_0\right\rangle + \left|00..1\right\rangle\left|\rho_1\right\rangle + ... + \left|11..1\right\rangle\left|\rho_{2^{n-1}-1}\right\rangle \end{align*}\end{split}\]

이제 각 단일 큐비트 상태 \(\left|\rho_0\right\rangle, ..., \left|\rho_{2^{n-1}-1}\right\rangle\) 는 적절한 각도 \(\phi\) and \(\theta\) 를 찾아내므로써 \(\left|0\right\rangle\) 로 변환할 수 있다. LSB의 얽힘을 풀어 나가는 이 과정을 모든 상태 동시에 수행하는 유니터리는 다음과 같다.

\[\begin{split}U = \begin{pmatrix} R_{y}(-\theta_0)R_{z}(-\phi_0) & & & &\\ & R_{y}(-\theta_1)R_{z}(-\phi_1) & & &\\ & . & & &\\ & & . & &\\ & & & & R_y(-\theta_{2^{n-1}-1})R_z(-\phi_{2^{n-1}-1}) \end{pmatrix}\end{split}\]

그러므로,

\[\begin{split}U\left|\psi\right\rangle = \begin{pmatrix} r_0e^{it_0}\\ r_1e^{it_1}\\ . \\ . \\ r_{2^{n-1}-1}e^{it_{2^{n-1}-1}} \end{pmatrix}\otimes\left|0\right\rangle\end{split}\]

U can be implemented as a 《quantum multiplexor》 gate, since it is a block diagonal matrix. In the quantum multiplexor formalism, a block diagonal matrix of size \(2^n \times 2^n\), and consisting of \(2^s\) blocks, is equivalent to a multiplexor with \(s\) select qubits and \(n-s\) data qubits. Depending on the state of the select qubits, the corresponding blocks are applied to the data qubits. A multiplexor of this kind can be implemented after recursive decomposition to primitive gates of cx, rz and ry.

[82]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.20.2
qiskit-aer0.10.4
qiskit-ignis0.7.1
qiskit-ibmq-provider0.19.1
qiskit0.36.2
qiskit-optimization0.3.2
qiskit-machine-learning0.4.0
System information
Python version3.10.4
Python compilerMSC v.1916 64 bit (AMD64)
Python buildmain, Mar 30 2022 08:38:02
OSWindows
CPUs6
Memory (Gb)15.869640350341797
Sat May 28 10:03:32 2022 Eastern Daylight Time

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: