演算子
このチュートリアルでは、量子力学で演算子を表すOperator
とPauliLabel
の2つのオブジェクトについて紹介します。この二つを使ってさまざまなオブザーバブルを構築することができます。QURI Partsでは、扱う演算子はパウリ文字列で構成されています。
PauliLabel
パウリ文字列は量子計算の世界ではどこにでも存在します。QURI PartsではPauliLabel
によって表現されます。このセクションではPauliLabel
は何で作られているか、またどのように作成するのかを説明します。まずは基本的な構成要素、パウリ行列から始めます。
パウリ行列
QURI Partsでは、パウリ行列はEnum
:SinglePauli
によって表現されます。これらは、直接計算に使われるオブジェクトではなく、単にPauliLabel
がどのようなパウリ行列を保持しているかを示すラベルです。
from quri_parts.core.operator import SinglePauli
assert SinglePauli.X == 1
assert SinglePauli.Y == 2
assert SinglePauli.Z == 3
パウリ文字列
前のとおり、パウリ文字列はPauliLabel
によって表現されます。インターフェースとどのように作るかについて紹介します。
インターフェース
QURI Partsでは、PauliLabel
はパウリ文字列を表現しています。具体的にいうと、tuple[qubit_index, SinglePauli]
のfrozenset
です。
class PauliLabel(frozenset(tuple[int, int])):
"""First int represents the qubit index and
the second represents a `SinglePauli`
"""
...
PauliLabel
を作成
PauliLabel
を作るには様々な方法があります。ここでは、pauli_label
関数を使用した1番簡単な方法を紹介します。基本的な使い方として、pauli_label
関数は2種類の入力タイプを受けつけます:
- パウリ文字列のような
str
- (量子ビットのインデックス、
SinglePauli
)のペアのシーケンス
str
を使用して作成
人間が読みやすいパウリ文字列のようなstr
を渡すことでPauliLabel
を作成することができます。
from quri_parts.core.operator import PauliLabel, pauli_label
print("Create without spacing:", pauli_label("X0 Y1 Z2 Z3 X4 Y5"))
print("Create with spacing: ", pauli_label("X 0 Y 1 Z 2 Z 3 X 4 Y 5"))
#output
Create without spacing: X0 Y1 Z2 Z3 X4 Y5
Create with spacing: X0 Y1 Z2 Z3 X4 Y5
パウリ行列の順序は問題ではないことに注意してください。
print("Create with X0 Y1 Z2:", pauli_label("X0 Y1 Z2"))
print("Create with X0 Z2 Y1:", pauli_label("X0 Z2 Y1"))
print(pauli_label("X0 Y1 Z2") == pauli_label("X0 Z2 Y1"))
#output
Create with X0 Y1 Z2: X0 Y1 Z2
Create with X0 Z2 Y1: X0 Y1 Z2
True
(qubit_index, SinglePauli)
のシーケンスを作成
(qubit_index, SinglePauli)
のシーケンスをpauli_label
関数に渡すことでもPauliLabel
を作成することができます。
print(pauli_label([(0, SinglePauli.X), (1, SinglePauli.Z)]))
print(pauli_label(zip((0, 1), (SinglePauli.X, SinglePauli.Z))))
#output
X0 Z1
X0 Z1
PAULI_IDENTITY
は特別なPauliLabel
です。これは、恒等演算子を表し、エントリーのないPauliLabel
です。
from quri_parts.core.operator import PAULI_IDENTITY
# PauliLabel() represents an empty `frozenset`.
print(PauliLabel() == PAULI_IDENTITY)
#output
True
PauliLabel
が提供するメソッド
PauliLabel
はそれ自身に関する情報をもたらすいくつかのメソッドを提供します。
index_and_pauli_id_list
: (list[qubit index], list[SinglePauli
])のタプルを返すプロパティ
pauli_label("X0 Y1 Z2").index_and_pauli_id_list
#output
([0, 1, 2], [<SinglePauli.X: 1>, <SinglePauli.Y: 2>, <SinglePauli.Z: 3>])
qubit_indices
:PauliLabel
が作用する量子ビットのリスト
pauli_label("X0 Y1 Z2").qubit_indices()
#output
[0, 1, 2]
pauli_at
: 特定の量子ビット上のパウリ行列。特定の量子インデックス上の演算子が恒等演算子ならば、None
を返す。
print(pauli_label("X0 Y1 Z2").pauli_at(0))
print(pauli_label("X0 Y1 Z2").pauli_at(1))
print(pauli_label("X0 Y1 Z2").pauli_at(2))
print(pauli_label("X0 Y1 Z2").pauli_at(3))
#output
SinglePauli.X
SinglePauli.Y
SinglePauli.Z
None
演算子
ここでは、Operator
オブジェクトを紹介します。Operator
オブジェクトはPauliLabel
の複素線形結合を表します。
インターフェース
QURI Partsでは、PauliLabel
をキー、複素数の係数をバリューとする辞書として実装されています。したがって、辞書を使用してOperator
を作成することができます。
from quri_parts.core.operator import Operator
op = Operator(
{
PAULI_IDENTITY: 8 + 1j,
pauli_label("X0 Y2"): -3
}
)
print(op)
#output
(8+1j)*I + -3*X0 Y2
Operator
の演算
Operator
に項を追加するには、add_term
メソッドを使用します。追加したいPauliLabel
がすでにOperator
に存 在する場合、その係数が更新されます。更新後の係数が0である場合、PauliLabel
はOperator
から削除されます。
op = Operator(
{
PAULI_IDENTITY: 8 + 1j,
pauli_label("X0 Y2"): -3
}
)
print(op, "\n")
# Add a new term to the Operator
pl, coeff = pauli_label("Y0 Z3"), 10+1j
print(f"Add {coeff} * {pl}:")
op.add_term(pl, coeff)
print(op, "\n")
# Add a `PauliLabel` that already exists in `Operator` to update the coefficient
pl, coeff = PAULI_IDENTITY, -6
print(f"Add {coeff} * {pl}:")
op.add_term(pl, coeff)
print(op, "\n")
# Add a `PauliLabel` that already exists in `Operator` to cancel the term
pl, coeff = pauli_label("X0 Y2"), 3
print(f"Add {coeff} * {pl}:")
op.add_term(pl, coeff)
print(op)
#output
(8+1j)*I + -3*X0 Y2
Add (10+1j) * Y0 Z3:
(8+1j)*I + -3*X0 Y2 + (10+1j)*Y0 Z3
Add -6 * I:
(2+1j)*I + -3*X0 Y2 + (10+1j)*Y0 Z3
Add 3 * X0 Y2:
(2+1j)*I + (10+1j)*Y0 Z3
2種類のプロパティが存在します:
- n_terms:
Operator
の項の個数 - constant:
Operator
のPAULI_IDENTITY
の係数を返します。PAULI_IDENTITY
が存在しない場合は0を返します。
op = Operator(
{
PAULI_IDENTITY: 8 + 1j,
pauli_label("X0 Y2"): -3
}
)
print("n_terms:", op.n_terms)
print("constant:", op.constant)
#output
n_terms: 2
constant: (8+1j)
Operator
オブジェクトも基本的な算術のための複数のメソッドを提供しています。
op1 = Operator({pauli_label("X0 Z1"): 8j})
op2 = Operator({pauli_label("Y1"): -4})
print("op1 = ", op1)
print("op2 = ", op2)
# Addition
print("")
print("Addition:")
print("op1 + op2", "=", op1 + op2)
# Subtraction
print("")
print("Subtraction:")
print("op1 - op2", "=", op1 - op2)
# Scalar Multiplication
print("")
print("Scalar Multiplication:")
print("op1 * 3j", "=", op1 * 3j)
# Scalar Division
print("")
print("Scalar Division:")
print("op1 / 2j", "=", op1 / 2j)
# Operator Multiplication
print("")
print("Operator Multiplication:")
print("op1 * op2", "=", op1 * op2)
print("op2 * op1", "=", op2 * op1)
# Hermitian conjugation
print("")
print("Hermition Conjgation:")
print("op1^†", "=", op1.hermitian_conjugated())
print("op2^†", "=", op2.hermitian_conjugated())
#output
op1 = 8j*X0 Z1
op2 = -4*Y1
Addition:
op1 + op2 = 8j*X0 Z1 + -4*Y1
Subtraction:
op1 - op2 = 8j*X0 Z1 + 4*Y1
Scalar Multiplication:
op1 * 3j = (-24+0j)*X0 Z1
Scalar Division:
op1 / 2j = (4+0j)*X0 Z1
Operator Multiplication:
op1 * op2 = (-32+0j)*X0 X1
op2 * op1 = (32+0j)*X0 X1
Hermition Conjgation:
op1^† = -8j*X0 Z1
op2^† = -4*Y1
特別なOperator
、0演算子を表すzero()
も存在します。これは空の辞書によって作成されたOperator
です。
from quri_parts.core.operator import zero
zero_operator = zero()
print(zero_operator == Operator())
#output
True
ヘルパー関数
その他にも、Operator
にいくつかの演算機能を提供する様々なヘルパー関数があります。ここではそれらを紹介します:
is_hermitian
commutator
truncate
is_ops_close
get_sparse_matrix
これらの例を以下で説明します:
is_hermitian
is_hermitian
はOperator
がエルミートであるかどうかをチェックします。
from quri_parts.core.operator import is_hermitian
op = Operator({pauli_label("X0"): 1})
print(f"{op} is hermitian:", is_hermitian(op))
op = Operator({pauli_label("X0"): 1j})
print(f"{op} is hermitian:", is_hermitian(op))