OQDのテンソル積、量子ビットを組み合わせる式と行列積の違い

青と桃色の半透明パネルの組と、四つの淡色タイルを組み合わせた面を並べた抽象画。

量子プログラムの式に、掛け算らしい記号が二つ出てきます。Open Quantum Design(OQD)のAnalogインターフェースでは、演算子どうしを * でつなぐ場合と、@ でつなぐ場合で意味が変わります。同じ量子ビットへの作用を重ねるのか、別々の量子ビットを一つの系として扱うのか。この違いを見失うと、式の見た目が似ていても、表している対象まで変わってしまいます。[1、2]

手がかりになるのが、テンソル積です。二つの量子ビットを組み合わせるとき、何を掛け、何の順番を保っているのでしょうか。OQDの記法を、手で追える小さな行列から見ていきます。

簡単にまとめると?

  • OQDの演算子では * が演算子の積、@ がテンソル積です。同じ空間で作用を重ねることと、別々の空間を組み合わせることを分けます。
  • 1量子ビットの2×2行列を二つテンソル積にすると、2量子ビットを表す4×4行列になります。どちらの量子ビットへ作用するかは、因子の並び順で決まります。
  • 式を作ること、数値として計算すること、実機で実行することは別です。積の記号だけで、量子もつれや計算性能を判断することもできません。

まず「どの空間の演算か」をそろえる

1量子ビットの状態は、|0> と |1> を基準にした二つの複素数の成分で表せます。この基準を基底と呼びます。状態を変換する演算子は、この二つの成分を受け取り、別の二つの成分へ写す2×2行列として表せます。

ここではパウリ演算子のXとZ、何も変えない恒等演算子Iを使います。Xは基底状態0と1を入れ替え、Zは0をそのままにして1の成分の符号を反転します。Zは、1を0へ変える演算ではありません。

X = [0  1]    Z = [1   0]    I = [1  0]
    [1  0]        [0  -1]        [0  1]

以下では状態を列ベクトルで書き、行列は左から掛けます。この約束では、積XZは右側のZを先に、左側のXを後に作用させる式です。二つとも同じ1量子ビットの空間で働くので、結果も2×2行列になります。

行列積は、同じ対象に作用を重ねる

実際に掛けると、XZとZXは次のようになります。

XZ = [0  -1]    ZX = [ 0  1]
     [1   0]         [-1  0]

二つは同じ行列ではなく、XZ = −ZX です。演算の順番を交換しても同じになることを「可換」といいますが、このXとZは可換ではありません。OQDの PauliX() * PauliZ() は、このような演算子の積を表す式として読めます。[2、3]

ただし、この例の符号差を、そのまま「順番を変えると測定結果が変わる」と説明するのは正確ではありません。XZとZXをそれぞれ同じ状態へ単独で適用した結果は、状態全体に−1が掛かった違いになります。これは全体位相の差で、この二つの出力だけを比べても測定確率は変わりません。ここで確かめているのは、行列としての積と順序の関係です。

テンソル積は、二つの対象を一緒に扱う

今度は量子ビットを二つ用意します。基底は |00>、|01>、|10>、|11> の四つです。左の数字を第1量子ビット、右を第2量子ビットとします。一般の状態は、この四つに対応する複素数の成分で表すので、演算子の行列は4×4になります。

この空間を組み立てるのがテンソル積です。行列で書いたテンソル積はクロネッカー積とも呼ばれます。X⊗Zなら、Xの各要素を、その数を掛けたZという2×2の塊に置き換えます。Xの0と1に合わせて、ゼロの塊とZの塊が並ぶため、次の4×4行列になります。

          [ 0  0 │ 1   0 ]
X ⊗ Z =  [ 0  0 │ 0  -1 ]
          [──────┼───────]
          [ 1  0 │ 0   0 ]
          [ 0 -1 │ 0   0 ]

OQDではこれを PauliX() @ PauliZ() と書きます。左のXは第1量子ビット、右のZは第2量子ビットへの作用です。この左右は、先ほどの行列積の「先に・後に」とは異なり、どの対象を担当するかを示しています。[1–3]

上段は同じ1量子ビットにZ、Xの順で作用するXZと、別々の量子ビットにXとZを対応させるXテンソルZの比較。下段は基底00、01、10、11がそれぞれ10、マイナス11、00、マイナス01に写る対応を示す。
図1 行列積の順序と、テンソル積の対象の並びを分けて読む。基底順は |00>、|01>、|10>、|11>。上段の大きさは行列の次元を示す。下段の符号は状態成分の符号で、測定値が負になる意味ではない。OQDの演算子の定義[1–3]を手がかりに、上記の行列から作成した説明図。画像を選ぶと原寸で表示できます。

たとえば |01> なら、Xが第1量子ビットの0を1へ写し、Zが第2量子ビットの1に−1を掛けるので、−|11> になります。一方、|00> は |10> です。符号の有無は第2量子ビットに依存します。入力の四つの基底すべてを調べると、行列の各列も確かめられます。

何もしないIにも、対象を指定する役割がある

2量子ビットのうち一つだけへXを作用させたいときも、全体を表す式には二つ分の場所が必要です。第1だけならX⊗I、第2だけならI⊗Xと書きます。Iは値を変えませんが、「ここにある量子ビットはそのままにする」という指定を保ちます。

式 対象 |00>への作用 行列の大きさ
XZ 同じ1量子ビット 2量子ビットの基底には直接対応しない 2×2
X⊗I 第1を反転、第2は維持 |10> 4×4
I⊗X 第1は維持、第2を反転 |01> 4×4
X⊗Z 第1を反転、第2の1成分の符号を反転 |10> 4×4

テンソル因子の位置を入れ替えて、X⊗IをI⊗Xに変えると、同じ入力 |00> からでも異なる基底状態へ写ります。テンソル積の因子を並べ替えるときには、量子ビットの対応まで変えていないか確認が必要です。なお、別々の対象へ働くX⊗IとI⊗Zを行列積で順に作用させる場合は、どちらの順でもX⊗Zになります。同じ「順番」でも、対象の配置と作用の順序では意味が違います。

OQDでこのIを使うときは、量子ビット用の PauliI() を選びます。同じソースにある Identity() は、ボソンという別の自由度のFock空間に対する恒等演算子です。名前の印象だけで置き換えず、式がどの種類の対象を表すかをそろえます。[2、3]

OQDの式は、まず演算の構造を記録する

OQDの公開実装では、演算子どうしの * は OperatorMul、@ は OperatorKron という節を作ります。数を掛ける 2 * PauliX() は OperatorScalarMul です。掛けた値や左右の演算子を保持する、抽象構文木(AST)という構造になっています。数との掛け算まで、量子ビットを増やす操作になるわけではありません。[3]

つまり、ここでの @ の意味はOQDの演算子型が定めています。別の数値配列の記法をそのまま持ち込んで読まず、「何の型の、どの演算か」を確認すると混乱を減らせます。本記事の行列は、明示した基底とパウリ行列から独立に計算したものです。OQDを実行した数値出力や、量子実機の測定結果ではありません。

式が組めることと、目的に合う物理モデルになっていることも別です。たとえば、エネルギーや相互作用を記述するハミルトニアンと、それによる時間発展で状態を変える演算は役割が異なります。積の記法だけから、どの実機で動くか、誤差がどれほどか、計算が速いかまでは分かりません。

積の記号から量子もつれを決めつけない

X⊗Zを見て「二つの量子ビットが結び付くから、必ずもつれが生じる」と考えるのも早計です。X⊗Zそのものを状態への演算として使うなら、それぞれの対象へXとZを別々に作用させたものです。最初の状態が |a>⊗|b> のように二つに分けて書けるなら、出力も (X|a>)⊗(Z|b>) と分けて書けます。この作用だけで、もつれていない純粋状態がもつれた状態になるわけではありません。

一方で、同じX⊗Zという式をハミルトニアンの一項に使う場合、調べるべき対象は、そのモデルと時間発展を含む全体です。「積作用素をそのまま適用した話」と混ぜないことが大切です。式を読むときは、まず対象の数と種類、次に基底と因子の並び、最後に式の役割を確かめる。この三つをそろえると、短い * と @ の違いが、何を計算しようとしているかの違いとして見えてきます。

出典

  1. Open Quantum Design, Analog interface — Explanation(2026年10月17日JSTに取得した保存版)。量子自由度と演算の種類。
  2. Open Quantum Design, Analog interface — API Reference(同日取得)。Pauli、Ladder、Operatorの定義。
  3. Open Quantum Design, oqd-core / operator.py(commit 4f34f34e に版を固定)。演算子の構文木と各記号の実装。Web解説は取得時の内容、コードはこの固定版を参照しています。