注釈
当ページは tutorials/circuits/3_summary_of_quantum_operations.ipynb から生成されました。
量子操作の概要¶
このセクションでは、Qiskit Terraで利用可能なさまざまな操作について説明します。これらは次のとおりです。
単一量子ビットゲート
複数量子ビットゲート
測定
リセット
条件文
状態の初期化
また、以下の 3 つの異なるシミュレーターの使用方法についても説明します。
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')
単一量子ビット量子状態¶
単一量子ビット量子状態は次のように記述できます。
ここでの \(\alpha\) と \(\beta\) は複素数です。測定において、ビットが \(\left|0\right\rangle\) になる確率は \(|\alpha|^2\) 、\(\left|1\right\rangle\) になる確率は \(|\beta|^2\) です。ベクトルとしては、次のように記述できます。
注記、確率の保存の \(|\alpha|^2+ |\beta|^2 = 1\) と、 \(\left|\psi\right\rangle := e^{i\delta} \left|\psi\right\rangle\) により絶対的な位相は検出できないことから、単一量子ビットの量子状態を記述するためには2つの実数のみを必要とします。
便利な表現として、
ただし、\(0\leq \phi < 2\pi\) 、および \(0\leq \theta \leq \pi\) とします。これより、量子ビット状態( \(\mathbb{C}^2\) )と、単位球面上の点( \(\mathbb{R}^3\) )との間に 1 対 1 の対応があることが明らかとなっています。 これは量子ビット状態のブロッホ球表現と呼ばれます。
通常、量子ゲート/操作は行列として表されます。1量子ビットに作用するゲートは、 \(2\times 2\) ユニタリ行列 \(U\) によって表されます。 量子ゲートの作用は、ゲートを表す行列を量子状態を表すベクトルにかけることによって分かります。
一般的なユニタリは、\(\left|0\right\rangle\) を上記の状態にできなければなりません。 これは、
ここでの \(a\) および \(b\) は、すべての \(0\leq\theta\leq\pi\) および \(0\leq \phi<2\pi\) に対して \(U^\dagger U = I\) となるような制約を持つ複素数です。これにより3つの制約が与えられ、\(0\leq \lambda<2\pi\) において \(a\rightarrow -e^{i\lambda}\sin(\theta/2)\) および \(b\rightarrow e^{i\lambda+i\phi}\cos(\theta/2)\) となり、次式が得られます。
これは単一量子ビットユニタリの最も一般的な形式です。
単一量子ビットゲート¶
使用可能な単一量子ビットゲートは次のとおりです。- Uゲート - Pゲート - 恒等ゲート - パウリゲート - クリフォードゲート - \(C3\) ゲート - 標準回転ゲート
ユニタリ行列を計算するためのバックエンドである unitary_simulator
が用意されています。
[3]:
q = QuantumRegister(1)
Uゲート¶
Qiskit では、 \(u\) ゲートを使用して一般のユニタリーにアクセスできます。このゲートには、次の行列形式があります。
[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メソッド \(u1\) 、 \(u2\) 、および \(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)\) ゲートは次の行列形式を持ちます。
これは、量子位相の適用に役立ちます。
[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]])
恒等ゲート¶
恒等ゲートは、 \(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]])
パウリゲート¶
\(X\): ビット・フリップ・ゲート¶
ビット・フリップ・ゲート \(X\) は次のように定義されています。
[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\) ゲートは次のように定義されています。
[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\) は、次のように定義されます。
[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]])
クリフォードゲート¶
アダマールゲート¶
[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\) (または、 \(\sqrt{Z}\) フェーズ) ゲート¶
[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}\) フェーズの共役 ) ゲート¶
[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}\) フェーズ) ゲート¶
[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}\) フェーズの共役 ) ゲート¶
[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\}\) の周囲で回転を定義するゲートが定義されているものです。
X 軸まわりの回転¶
[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 軸まわりの回転¶
[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 軸まわりの回転¶
ここでは、グローバル位相 \(e^{-i \phi/2}\) によって \(p\) とは異なるため、同等のものを使用していることに注意してください。
[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]])
これは、グローバル・フェーズのみ異なることに注意してください。
複数量子ビットゲート¶
数学的準備¶
量子コンピューターの次元は、量子ビットの数に対して指数関数的に大きくなります。\(n\) 量子ビットの場合、複素数ベクトル空間は \(d=2^n\) 次元になります。複数量子ビット系の状態を表すためには、テンソル積が演算子と基底ベクトルの”結合”として使われます。
2量子ビットシステムを考える所から始めてみましょう。それぞれ片方の量子ビットに作用する \(A\) と \(B\) という2つの演算子を用意し、2つの量子ビットに作用する結合演算子 \(A \otimes B\) は、
ここでは、 \(A_{jk}\) と \(B_{lm}\) は \(A\) と \(B\) それぞれの行列成分になります。
類似的に、2量子ビット系の基底ベクトルは、単一量子ビットでの基底ベクトルのテンソル積を使って形成されます。
ここでは、基底ベクトルの略記を導入します。\(\left|0\right\rangle \otimes \left|0\right\rangle\) は \(\left|00\right\rangle\) と表現されます。 \(n\) 量子ビット系の状態は、単一量子ビットベクトルの \(n\) 階のテンソル積を使って書き下すことができます。2量子ビット系の基底ベクトルは、4次元になります。前に述べたように、一般に、 \(n\) 量子ビット系の基底ベクトルは、 \(2^{n}\) 次元になります。
Qiskitにおける基底ベクトル表記の順番¶
物理コミュニティでは、複数量子ビット系の量子ビットは通常、テンソル積の左端に最初の量子ビットがあり、右端に最後の量子ビットが来るように順序づけられています。例えば、最初の量子ビットの状態が \(\left|0\right\rangle\) で、2番目のものが \(\left|1\right\rangle\) の状態のとき、それらの結合状態は \(\left|01\right\rangle\) のようになります。Qiskitでは少し異なる量子ビットの順番をとります。左に最上位ビット(most significant bit、MSB)をとり、右に最下位ビット(least significant bit、LSB)をとるように表されます(リトルエンディアン)。これは古典コンピュータにおけるビット列表現と似ていて、測定が行われた後のビット列から整数への変換が容易になります。上記の例では、結合状態は \(\left|10\right\rangle\) と表されます。重要なのは、以下で説明されるように、*複数量子ビットの状態の表現のこの変更が、Qiskitでの複数量子ビットゲートの表現方法に影響がある*ということです。
Qiskitで使用される表現は、基底ベクトルをそれらが表す整数の昇順で列挙します。たとえば、2量子ビット系の基底ベクトルは、\(\left|00\right\rangle\)、\(\left|01\right\rangle\)、\(\left|10\right\rangle\)、および \(\left|11\right\rangle\) の順序になります。基底ベクトルをビット文字列と見なし、それぞれを整数の 0、1、2、3 にエンコードします。
量子ビットに対する制御操作¶
複数 qubit ゲートに共通なものとして、一つの qubit にゲートを適用し、別の qubit の状態に応じて制御するというものがあります。例えば、最初の qubit が \(\left|0\right\rangle\) の時に、2つ目の qubit の状態をフリップしたい場合があります。このようなゲートは 制御ゲート と知られています。標準的な複数 qubit ゲートは 2 qubit ゲートと 3 qubit ゲートから構成されています。2 qubit ゲートは: - 制御パウリゲート -制御アダマールゲート - 制御回転ゲート - 制御位相ゲート - 制御 u3 ゲート -スワップゲート です。
3量子ビットゲートは: - トフォリゲート -フレドキンゲートです。
2量子ビット(two-qubit) ゲート¶
多くの2量子ビットゲートは制御タイプのものです(SWAP ゲートは例外になります)。一般的に、制御2量子ビットゲート \(C_{U}\) は 最初の量子ビットが \(\left|1\right\rangle\) の時に、2つ目の量子ビットに対して単独量子ビット \(U\) を適用します。 \(U\) が以下の行列表示であるとします。
\(C_{U}\) の動作は次のように計算できます。2量子ビット系の基底ベクトルは \(\left|00\right\rangle、\left|01\right\rangle、\left|10\right\rangle、\left|11\right\rangle\) の順序で並べられることを思い出してください。 制御量子ビット は 0量子ビット(qubit 0) であると仮定します(Qiskitの慣例によれば、これはテンソル積の 右側 のひとつです)。もし制御量子ビットが \(\left|1\right\rangle\) であれば、\(U\) を ターゲット に適用する必要があります(テンソル積の左側の1量子ビット(qubit 1)です)。したがって、\(C_{U}\) のもとにおける基底ベクトルは
行列形式では、 \(C_{U}\) の作用は
これらのマトリックス要素を処理するには、
として、 \(C_{U}\) (上記のもの)を作用させ、内積を計算することで導出されます。
以下の例に示すように、この操作は Qiskit では cU(q[0],q[1])
として実装されています。
もし 量子ビット1 が制御で、量子ビット 0 が ターゲット の場合には、基底ベクトルは次のもので変換されます。
この場合の \(C_{U}\) の行列表示は次のようになります。
[37]:
q = QuantumRegister(2)
制御パウリゲート¶
制御 X (制御NOT) ゲート¶
制御NOTゲートは、制御量子ビットが \(\left|1\right\rangle\) 状態のとき、 ターゲット
量子ビットを反転します。もしMSBを制御ビットとした時(例: cx(q[1],q[0])
)、行列は以下の通りになります。
しかし、LSBが制御量子ビットの場合 (例えば、cx(q[0],q[1])
)、このゲートは次の行列と同等です。
[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\) ゲート¶
制御量子ビットが MSB の場合、ターゲット量子ビットに \(Y\) ゲートを適用します。
あるいはLSBを制御にした場合
[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]])
制御 \(Z\) (または、制御位相反転)ゲート¶
同様に、制御量子ビットが \(\left|1\right\rangle\) である場合には、制御された Z ゲートがターゲット量子ビットの位相を反転させます。 この行列は、 MSB または LSB が制御量子ビットかどうかに関係なく、同じになります。
[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]])
制御アダマールゲート¶
制御量子ビットが \(\left|1\right\rangle\) のとき、ターゲット量子ビットに \(H\) ゲートを適用します。制御がLSB量子ビットの時、以下の通りになります。
[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]])
制御回転ゲート¶
Z軸周りの制御された回転¶
制御量子ビット(ここではLSB)が \(\left|1\right\rangle\) の場合、ターゲット量子ビットでZ軸周りの回転を実行します。
[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が制御量子ビットであるかどうかに関係なく同じに見えます。
[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\) の場合、ターゲット量子ビットに対して制御-\(u\) 回転を実行します。
[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 ゲート¶
SWAPゲートは2つの量子ビットを交換します。それは次のように基底ベクトルを変換します。
行列表現は以下の通りです。
[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]])
3量子ビット・ゲート¶
一般に使用されている2つの3量子ビット・ゲートがあります。3量子ビットにおいて、基底ベクトルは以下の順番になります。
ビット・ストリングは、整数 \(0,1,2,\cdots, 7\) を表します。繰り返しになりますが、Qiskitは最初の量子ビットがテンソル積の右端にあり、3番目の量子ビットが左端にあるという表現を使用します:
Toffoliゲート( \(ccx\) ゲート)¶
Toffoliゲート は、最初の2つの量子ビット(LSB)がどちらも \(\left|1\right\rangle\) のとき、3番目の量子ビットを反転します:
行列形式では、トフォリゲートは
[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]])
制御SWAPゲート (Fredkinゲート)¶
Fredkinゲート、または 制御SWAPゲート は、最初の量子ビット(LSB)が \(\left|1\right\rangle\) のとき、2番目と3番目の量子ビットを交換します:
行列形式では、Fredkinゲートは以下の通りになります。
[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]])
非ユニタリ操作¶
ここまで、量子回路におけるすべてのユニタリ操作について見てきましたが、非ユニタリ操作についても見ていきましょう。ここでは、測定、キュービットのリセット、そして古典的条件付き操作が含まれます。
[62]:
q = QuantumRegister(1)
c = ClassicalRegister(1)
測定¶
我々は測定が行われるまで量子コンピューターにおける全ての情報を知ることはできません。量子状態は、標準基底に射影されます。下の2つの例は基底状態に準備した回路と量子コンピューターが重ね合わせ状態に準備した回路になります。
[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}
シミュレーターは古典レジスタが0を返す確率が100%であることを予測しています。
[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}
シミュレーターは古典レジスタが0もしくは1を返す確率が50%であることを予測しています。
リセット¶
リセット演算子はキュービットを計算の途中で \(\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}
ここで、両方の回路においてシミュレーターは出力が100%の確率で0状態であることを常に予測することが確認できます。
条件付き操作¶
古典レジスタの状態に応じて操作を行うことも可能です。
[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\) 量子ビットの任意の状態は、\(2^n\) の振幅のベクトルで指定できます。ここで、振幅 - ノルム - 平方の合計は1です。例えば、次の3量子ビットの状態を用意するには以下のようにします:
[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(忠実度) は、2つの状態が同じかどうかを確認するのに役立ちます。 (純粋な)量子状態 \(\left|\psi_1\right\rangle\) と \(\left|\psi_2\right\rangle\),の場合、Fidelity (忠実度) 度は
2つの状態が同じ場合に限り、忠実度は \(1\) に等しくなります。
[81]:
state_fidelity(desired_vector,qc_state)
[81]:
1.0000000000000004
更に詳細な説明:¶
裏では、どのようにして目的の状態が生成されるのでしょうか。 複数の方法がありますが、 Qiskitは Shendeらによって提案された方法 を使っています。 これは、量子レジスタが我々の望む状態から始まったと仮定し、それを \(\left|00..0\right\rangle\) の状態にする回路を構築するアイデイアです。 初期化回路はこの回路の逆になります。
任意の量子状態を計算基底のゼロ状態にするために、1つずつレジスターから量子ビットのもつれをほどく反復的な手順を行ないます。任意の1量子ビット状態 \(\left|\rho\right\rangle\) は次のようにZ軸まわりの \(\phi\) 度回転とY軸まわりの \(\theta\) 度回転を用いることで \(\left|0\right\rangle\) 状態にすることができます。
ここからは、1量子ビットの代わりに \(n\) 量子ビットを扱うと、状態ベクトルを分解して最下位ビット(LSB)方式で分離する必要があります。
ここで、それぞれの1量子ビット状態 \(\left|\rho_0\right\rangle, ..., \left|\rho_{2^{n-1}-1}\right\rangle\) は、 上の式に従って適切な角度 \(\phi\) と \(\theta\) を見つけることで \(\left|0\right\rangle\) に取れます。 すべての状態でこれを同時に行うことは、最下位ビットの量子もつれをほどくユニタリ行列は次のようになります:
よって
これはブロック対角行列なので、Uは「量子マルチプレクサー」ゲートとして実装できます。量子マルチプレクサーのフォーマリズムでは \(2^s\) 個のブロックから成る \(2^n \times 2^n\) のブロック対角行列は、\(s\) 個の選択制御量子ビットと \(n-s\) 個のデータ量子ビットをもつマルチプレクサーと等価です。選択制御量子ビットの状態に依って、対応するブロックがデータ量子ビットに適用されます。この種のマルチプレクサーはcx、rz、ryといった基本ゲートへ再帰的に分解することで実装することができます。
[82]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.20.2 |
qiskit-aer | 0.10.4 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.1 |
qiskit | 0.36.2 |
qiskit-optimization | 0.3.2 |
qiskit-machine-learning | 0.4.0 |
System information | |
Python version | 3.10.4 |
Python compiler | MSC v.1916 64 bit (AMD64) |
Python build | main, Mar 30 2022 08:38:02 |
OS | Windows |
CPUs | 6 |
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.
[ ]: