前書き
本記事では、Quantum Katas の UnitaryPatterns について解説します。Q# や Quantum Katas って何よという方は Quantum Katas で始める量子コンピュータ入門 |0⟩ をご参照ください。
この解説を書いているときは既に Grover や QPE を始めとしたアルゴリズムの Kata が終わっているタイミングなのですが、今になってかなり基本的なユニタリ変換に関する表題の Kata が公開されました。Quantum Katas 自体は別に解く順番を案内しているわけではなく、また、この解説の連番も私が解いた順に適当に降っているだけなのですが、流石に QFE の後にこれをやるのはいかがという感じなので、今回は第 |1⟩ 段 と |2⟩ 段の重ね合わせとしています。テンソル積に馴染みがない方はこの Kata から始めるのもありかも、というくらい基本的な内容です。それほど難しくないので進めていきましょう。
Task 1. Main diagonal
問題にもある通り、何もしなくてよいのでスキップします。
Task 2. All-non-zero matrix
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: すべての要素が非 0 であるユニタリ行列で処理を行え。例えば N=2 なら
\begin{bmatrix}
* & * &* & * \\
* & * &* & * \\
* & * &* & * \\
* & * &* & *
\end{bmatrix}
となる。要素の値は問わない。
解説
すべての要素に値が入っているユニタリ行列で処理をすればよい、という問題です。全部の要素に値が入っているとすればアダマールゲートですよね。別の同じ値である必要もないので、位相のズレも意識する必要はありません。アダマールゲートのテンソル積は
H^{\otimes n+1} = H \otimes H^{\otimes n} =
\begin{bmatrix}
H^{\otimes n} & H^{\otimes n} \\
H^{\otimes n} & -H^{\otimes n}
\end{bmatrix}
となりますから、最初の H がすべての値が非0という要件を満たすことより、今回の問題で必要となる要件が成り立つことも確かめられますね。回答配下です。
operation AllNonZero (qs : Qubit[]) : Unit {
ApplyToEach(H, qs);
}
Task 3. Quarters
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: ユニタリ行列を半分に分けたとき、左上と右下だけが非 0 となっている行列で処理を行え。例えば N=2 なら
\begin{bmatrix}
* & * &0 & 0 \\
* & * &0 & 0 \\
0 & 0 &* & * \\
0 & 0 &* & *
\end{bmatrix}
となる。要素の値は問わない。
解説
左上と右下だけは非0 で埋めつつ、それ以外は 0 にしたいとの問題です。全部非0で埋めるのはアダマールでできるので、先程と同様に考えると
I \otimes H^{\otimes n} =
\begin{bmatrix}
H^{\otimes n} & \mathbf 0 \\
\mathbf 0 & -H^{\otimes n}
\end{bmatrix}
で実現できそうです。要は、最上位ビット以外をアダマールで処理すればよい、ということになります。
operation Quarters (qs : Qubit[]) : Unit {
ApplyToEach(H,qs[0..Length(qs)-2]);
}
Task 4. Even chessboard pattern
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: 非 0 と 0 が交互に存在するユニタリ行列で処理を行え。例えば N=2 なら
\begin{bmatrix}
* & 0 &* & 0 \\
0 & * &0 & * \\
* & 0 &* & 0 \\
0 & * &0 & *
\end{bmatrix}
となる。要素の値は問わない。
解説
これも同じように考えましょう。まずは上記のパターンを作る方法を考えると、
H \otimes I =
\begin{bmatrix}
I & I \\
I & -I
\end{bmatrix} =
\begin{bmatrix}
1 & 0 &1 & 0 \\
0 & 1 &0 & 1 \\
1 & 0 &-1 & 0 \\
0 & 1 &0 & -1
\end{bmatrix}
でできました。あとはアダマールゲート H と $H \otimes I$ のテンソル積をずっと撮っていけば次元も増やせそうです。最初の I は処理不要なので、要は最下位ビット以外をアダマールで処理する、ということになります。回答は以下です。
operation EvenChessPattern (qs : Qubit[]) : Unit {
ApplyToEach(H,qs[1..Length(qs)-1]);
}
Task 5. Odd chessboard pattern
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: 非 0 と 0 が交互に存在するユニタリ行列で処理を行え。例えば N=2 なら
\begin{bmatrix}
0 &* & 0 &* \\
* &0 & * &0 \\
0 &* & 0 &* \\
* &0 & * &0
\end{bmatrix}
となる。要素の値は問わない。
解説
さっきとほぼ一緒です。パターンが反転しているので、最初に適用するゲートだけ $X$ に変えてやれば
H \otimes X =
\begin{bmatrix}
X & X \\
X & -X
\end{bmatrix} =
\begin{bmatrix}
0 & 1 &0 & 1\\
1 & 0 &1 & 0\\
0 & 1 &0 &-1 \\
1 & 0 &-1 & 0
\end{bmatrix}
となります。あとはアダマールゲートで処理すればいいですね。回答は以下です。
operation OddChessPattern (qs : Qubit[]) : Unit {
X(qs[0]);
ApplyToEach(H,qs[1..Length(qs)-1]);
}
Task 6. Anti-diagonal
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: 反対角行列で処理を行え。例えば N=2 なら
\begin{bmatrix}
0 & 0 &0 & * \\
0 & 0 &* & 0 \\
0 & * &0 & 0 \\
* & 0 &0 & 0
\end{bmatrix}
となる。要素の値は問わない。
解説
同じように考えます。反対角行列といえばぱっと思いつくのは X ゲートです。X のテンソル積は
X \otimes X^{\otimes n} =
\begin{bmatrix}
\mathbf 0 & X^{\otimes n} \\
X^{\otimes n} & \mathbf 0
\end{bmatrix}
となるので、これだけで条件が満たせそうですね。回答は以下です。
operation Antidiagonal (qs : Qubit[]) : Unit {
ApplyToEach(X,qs[0..Length(qs)-1]);
}
Task 7. 2x2 chessboard pattern
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: 2*2 を 1 つの単位として、非0と0が交互に存在するユニタリ行列で処理を行え。例えば N=3 なら
\begin{bmatrix}
* & * &0 & 0 & * & * &0 & 0\\
* & * &0 & 0 & * & * &0 & 0\\
0 & 0 &* & * & 0 & 0 &* & *\\
0 & 0 &* & * & 0 & 0 &* & *\\
* & * &0 & 0 & * & * &0 & 0\\
* & * &0 & 0 & * & * &0 & 0\\
0 & 0 &* & * & 0 & 0 &* & *\\
0 & 0 &* & * & 0 & 0 &* & *
\end{bmatrix}
となる。要素の値は問わない。
解説
まずは今回の単位となる 4*4 行列を作ります。と入っても、これは Task 3 の例そのままです。
I \otimes H =
\begin{bmatrix}
H & \mathbf 0 \\
\mathbf 0 & H
\end{bmatrix} =
\begin{bmatrix}
1 & 1 &0 & 0\\
1 & -1 &0 & 0\\
0 & 0\ & 1 & 1 \\
0 & 0 & 1 & -1
\end{bmatrix}
あとはこれにアダマールゲートをかけていけば良さそうです。これまでと違うのは、2 ビット目を除いた全ビットをアダマールゲートで処理する、ということになる点でしょうか。
operation ChessPattern2x2 (qs : Qubit[]) : Unit {
ApplyToEach(H,Exclude([1], qs));
}
Q# には指定した要素を除外した配列を取得する Exclude
という関数があるので、それで 2 ビット目だけを除外してみました。
Task 8. Two patterns
問題
入力: 任意の状態にある長さ $N$ の量子ビット
ゴール: 左上は Task 1.6、右下は Task1.2 のパターンを持ち、その他は 0 であるユニタリ行列で処理を行え。例えば N=2 なら
\begin{bmatrix}
0 & * &0 & 0 \\
* & 0 &0 & 0 \\
0 & 0 &* & * \\
0 & 0 &* & *
\end{bmatrix}
となる。要素の値は問わない。
解説
ちょっと毛色が変わりました。これまでは一様なパターンだったのですが、2つのパターンが入り交じるような状況になります。ただしそれほど深く考える必要はありません。行列の上半分と下半分でパターンが変わるので、要はこの 2 パターンは最上位ビットが 0 か 1 かで作り分ければ良さそうです。Controlled ファンクタ付きでそれぞれの Task と同じ処理をすればよさそうですね。回答は以下です。
operation TwoPatterns (qs : Qubit[]) : Unit {
// 0 のときの判定をするため、まずは再上位ビットを反転
X(qs[Length(qs)-1]);
// 最上位ビットを制御ビットとして、Task 1.6 と同じ処理を実施 (左上要素の用意)
(Controlled ApplyToEachC)([qs[Length(qs)-1]],(X,qs[0..Length(qs)-2]));
// 反転した最上位ビットを戻す
X(qs[Length(qs)-1]);
// 最上位ビットを制御ビットとして、Task 1.2 と同じ処理を実施 (右下要素の用意)
(Controlled ApplyToEachC)([qs[Length(qs)-1]],(H,qs[0..Length(qs)-2]));
}
まとめ
今回はユニタリ行列とテンソル積について学んできました。まだ問題が追加されそうな気もしますが、今日はここまでです。