メインコンテンツへスキップ

Operatorクラス

Package versions

このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以降を使用することをお勧めします。

qiskit[all]~=2.3.0

このページでは、Operatorクラスの使い方を説明します。Operatorクラスを含む、Qiskitにおける演算子表現の概要については、演算子クラスの概要をご参照ください。

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import CXGate, RXGate, XGate
from qiskit.quantum_info import Operator, Pauli, process_fidelity

クラスをOperatorに変換する

Qiskitの他のいくつかのクラスは、演算子の初期化メソッドを使用して直接Operatorオブジェクトに変換できます。例えば:

  • Pauliオブジェクト
  • GateおよびInstructionオブジェクト
  • QuantumCircuitオブジェクト

最後の点は、Operatorクラスをユニタリシミュレータとして使用し、シミュレータバックエンドを呼び出さずに量子回路の最終的なユニタリ行列を計算できることを意味します。回路にサポートされていない操作が含まれている場合は、例外が発生します。サポートされていない操作は、測定、リセット、条件付き操作、または行列の定義や行列の定義を持つゲートによる分解を持たないゲートです。

# Create an Operator from a Pauli object

pauliXX = Pauli("XX")
Operator(pauliXX)
Operator([[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],
[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],
input_dims=(2, 2), output_dims=(2, 2))
# Create an Operator for a Gate object
Operator(CXGate())
Operator([[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]],
input_dims=(2, 2), output_dims=(2, 2))
# Create an operator from a parameterized Gate object
Operator(RXGate(np.pi / 2))
Operator([[0.70710678+0.j        , 0.        -0.70710678j],
[0. -0.70710678j, 0.70710678+0.j ]],
input_dims=(2,), output_dims=(2,))
# Create an operator from a QuantumCircuit object
circ = QuantumCircuit(10)
circ.h(0)
for j in range(1, 10):
circ.cx(j - 1, j)

# Convert circuit to an operator by implicit unitary simulation
Operator(circ)
Operator([[ 0.70710678+0.j,  0.70710678+0.j,  0.        +0.j, ...,
0. +0.j, 0. +0.j, 0. +0.j],
[ 0. +0.j, 0. +0.j, 0.70710678+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, ...,
0. +0.j, 0. +0.j, 0. +0.j],
[ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,
0. +0.j, 0. +0.j, 0. +0.j],
[ 0.70710678+0.j, -0.70710678+0.j, 0. +0.j, ...,
0. +0.j, 0. +0.j, 0. +0.j]],
input_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2), output_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2))

回路でOperatorを使用する

ユニタリなOperatorsは、QuantumCircuit.appendメソッドを使用してQuantumCircuitに直接挿入できます。これにより、OperatorUnitaryGateオブジェクトに変換され、回路に追加されます。

演算子がユニタリでない場合は、例外が発生します。これはOperator.is_unitary()関数で確認でき、演算子がユニタリであればTrue、そうでなければFalseを返します。

# Create an operator
XX = Operator(Pauli("XX"))

# Add to a circuit
circ = QuantumCircuit(2, 2)
circ.append(XX, [0, 1])
circ.measure([0, 1], [0, 1])
circ.draw("mpl")

Output of the previous code cell

上記の例では、演算子はPauliオブジェクトから初期化されています。ただし、Pauliオブジェクトを回路に直接挿入することもでき、その場合は1量子ビットのPauliゲートのシーケンスに変換されます。

# Add to a circuit
circ2 = QuantumCircuit(2, 2)
circ2.append(Pauli("XX"), [0, 1])
circ2.measure([0, 1], [0, 1])
circ2.draw()
┌────────────┐┌─┐
q_0: ┤0 ├┤M├───
│ Pauli(XX) │└╥┘┌─┐
q_1: ┤1 ├─╫─┤M├
└────────────┘ ║ └╥┘
c: 2/═══════════════╩══╩═
0 1

Operatorを組み合わせる

演算子はいくつかの方法で組み合わせることができます。

テンソル積

2つの演算子 AABB を、Operator.tensor関数を使ってテンソル積演算子 ABA\otimes B に組み合わせることができます。AABB が両方とも1量子ビット演算子である場合、A.tensor(B) = ABA\otimes B では、サブシステム0に行列 BB、サブシステム1に行列 AA がインデックス付けされます。

A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.tensor(B)
Operator([[ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j],
[ 0.+0.j, -0.+0.j, 0.+0.j, -1.+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]],
input_dims=(2, 2), output_dims=(2, 2))

テンソル展開

密接に関連する操作としてOperator.expandがあり、テンソル積と同様に機能しますが逆の順序で行われます。したがって、2つの演算子 AABB に対して A.expand(B) = BAB\otimes A となり、サブシステム0に行列 AA、サブシステム1に行列 BB がインデックス付けされます。

A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.expand(B)
Operator([[ 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, -0.+0.j, -1.+0.j],
[ 0.+0.j, 0.+0.j, -1.+0.j, -0.+0.j]],
input_dims=(2, 2), output_dims=(2, 2))

合成

Operator.composeメソッドを使用して、2つの演算子 AABB を合成し、行列の乗算を実装することもできます。A.compose(B) は行列 B.AB.A を持つ演算子を返します。

A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.compose(B)
Operator([[ 0.+0.j,  1.+0.j],
[-1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))

composefrontキーワード引数を使用して、AA の前に BB を適用する逆順の合成も行えます。A.compose(B, front=True) = A.BA.B となります。

A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.compose(B, front=True)
Operator([[ 0.+0.j, -1.+0.j],
[ 1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))

サブシステムの合成

前述の合成では、第1の演算子 AA の全出力次元が、合成する演算子 BB の全入力次元と等しい必要があります(また、front=Trueで合成する場合は、BB の出力次元が AA の入力次元と等しい必要があります)。

composeqargsキーワード引数を使用して、front=Trueの有無にかかわらず、より大きな演算子の選択されたサブシステムにより小さな演算子を合成することもできます。この場合、合成されるサブシステムの関連する入出力次元が一致している必要があります。なお、より小さな演算子は常に compose メソッドの引数でなければなりません。

例えば、2量子ビットゲートを3量子ビット演算子と合成する場合:

# Compose XZ with a 3-qubit identity operator
op = Operator(np.eye(2**3))
XZ = Operator(Pauli("XZ"))
op.compose(XZ, qargs=[0, 2])
Operator([[ 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, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,
-1.+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,
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]],
input_dims=(2, 2, 2), output_dims=(2, 2, 2))
# Compose YX in front of the previous operator
op = Operator(np.eye(2**3))
YX = Operator(Pauli("YX"))
op.compose(YX, qargs=[0, 2], front=True)
Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.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, 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.-1.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, 0.+1.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, 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.+1.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, 0.+0.j, 0.+0.j, 0.+0.j]],
input_dims=(2, 2, 2), output_dims=(2, 2, 2))

線形結合

演算子は、加算、減算、複素数によるスカラー乗算などの標準的な線形演算子を使用して組み合わせることもできます。

XX = Operator(Pauli("XX"))
YY = Operator(Pauli("YY"))
ZZ = Operator(Pauli("ZZ"))

op = 0.5 * (XX + YY - 3 * ZZ)
op
Operator([[-1.5+0.j,  0. +0.j,  0. +0.j,  0. +0.j],
[ 0. +0.j, 1.5+0.j, 1. +0.j, 0. +0.j],
[ 0. +0.j, 1. +0.j, 1.5+0.j, 0. +0.j],
[ 0. +0.j, 0. +0.j, 0. +0.j, -1.5+0.j]],
input_dims=(2, 2), output_dims=(2, 2))

重要な点として、tensorexpandcomposeはユニタリ演算子のユニタリ性を保持しますが、線形結合はそうではありません。したがって、2つのユニタリ演算子を加算すると、一般的に非ユニタリ演算子になります。

op.is_unitary()
False

Operatorへの暗黙的な変換

以下のすべてのメソッドにおいて、2番目のオブジェクトがすでにOperatorオブジェクトでない場合、メソッドによって暗黙的にOperatorオブジェクトに変換されます。つまり、行列を明示的にOperatorに変換することなく直接渡すことができます。変換が不可能な場合は、例外が発生します。

# Compose with a matrix passed as a list
Operator(np.eye(2)).compose([[0, 1], [1, 0]])
Operator([[0.+0.j, 1.+0.j],
[1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))

Operatorを比較する

演算子は等値メソッドを実装しており、2つの演算子がほぼ等しいかどうかを確認するために使用できます。

Operator(Pauli("X")) == Operator(XGate())
True

これは演算子の各行列要素がほぼ等しいかどうかを確認していることに注意してください。グローバル位相が異なる2つのユニタリは等しいとみなされません。

Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
False

プロセスフィデリティ

量子情報モジュールのprocess_fidelity関数を使用して演算子を比較することもできます。これは2つの量子チャネルがどれほど近いかを表す情報理論的な量であり、ユニタリ演算子の場合はグローバル位相に依存しません。

# Two operators which differ only by phase
op_a = Operator(XGate())
op_b = np.exp(1j * 0.5) * Operator(XGate())

# Compute process fidelity
F = process_fidelity(op_a, op_b)
print("Process fidelity =", F)
Process fidelity = 1.0

プロセスフィデリティは一般に、入力演算子がユニタリ(または量子チャネルの場合はCP)である場合にのみ有効な近さの尺度であり、入力がCPでない場合は例外が発生します。

次のステップ

Recommendations
  • Operator APIリファレンスを参照してください。