Operator Flow¶
はじめに¶
Qiskit には、状態と演算子、合計、テンソル積、合成を表すクラスが用意されています。これらの代数的構成物により、演算子を表す式を構築することができます。
まずパウリ演算子を用いて構築する式を紹介します。 後続のセクションでは、より詳細な演算子と状態、それらがどのように表現されるか、そしてそれらを使って何ができるかについて説明します。 最後のセクションでは、状態を構築し、ハミルトニアンでそれを発展させ、観測可能な期待値を計算します。
パウリ演算子、和、合成、およびテンソル積¶
最も重要な基礎的演算子は、パウリ演算子です。パウリ演算子は、このように表現されます。
[1]:
from qiskit.aqua.operators import I, X, Y, Z
print(I, X, Y, Z)
I X Y Z
これらの演算子は係数を持つこともできます。
[2]:
print(1.5 * I)
print(2.5 * X)
1.5 * I
2.5 * X
これらの係数により、演算子は和の項として使用できます。
[3]:
print(X + 2.0 * Y)
SummedOp([
X,
2.0 * Y
])
テンソル積は、次のようにキャレット(脱字記号) で表されます。
[4]:
print(X^Y^Z)
XYZ
合成は``@`` 記号で表されます。
[5]:
print(X @ Y @ Z)
1j * I
In the preceding two examples, the tensor product and composition of Pauli operators were immediately reduced to the equivalent (possibly multi-qubit) Pauli operator. If we tensor or compose more complicated objects, the result is objects representing the unevaluated operations. That is, algebraic expressions.
例えば、2つの和を合成すると:
[6]:
print((X + Y) @ (Y + Z))
ComposedOp([
SummedOp([
X,
Y
]),
SummedOp([
Y,
Z
])
])
また、2つの和のテンソル積を取ると:
[7]:
print((X + Y) ^ (Y + Z))
TensoredOp([
SummedOp([
X,
Y
]),
SummedOp([
Y,
Z
])
])
上記の種類を詳しく見てみましょう。まずはパウリ演算子です。
[8]:
(I, X)
[8]:
(PauliOp(Pauli(z=[False], x=[False]), coeff=1.0),
PauliOp(Pauli(z=[False], x=[True]), coeff=1.0))
各パウリ演算子は PauliOp
のインスタンスで、qiskit.quantum_info.Pauli
のインスタンスをラップし、係数``coeff`` を追加します。一般的に, PauliOp
はパウリ演算子の加重テンソル積を表します.
[9]:
2.0 * X^Y^Z
[9]:
PauliOp(Pauli(z=[True, True, False], x=[False, True, True]), coeff=2.0)
パウリ演算子をブール値のペアとしてエンコーディングするには、qiskit.quantum_info.Pauli
のドキュメントを参照してください。
演算子を表現する全てのオブジェクトは、``PauliOp``のような「プリミティブ」でも代数式でも、係数を持ちます。
[10]:
print(1.1 * ((1.2 * X)^(Y + (1.3 * Z))))
1.1 * TensoredOp([
1.2 * X,
SummedOp([
Y,
1.3 * Z
])
])
以下では、Qiskitの演算子、状態、および量子アルゴリズムの構成要素をより幅広く詳しく見ていきます。
パートI: 状態関数と測定¶
Quantum states are represented by subclasses of the class StateFn
. There are four representations of quantum states: DictStateFn
is a sparse representation in the computational basis, backed by a dict
. VectorStateFn
is a dense representation in the computational basis backed by a numpy array. CircuitStateFn
is backed by a circuit and represents the state obtained by executing the circuit on the all-zero computational-basis state. OperatorStateFn
represents mixed states
via a density matrix. (As we will see later, OperatorStateFn
is also used to represent observables.)
便宜上、いくつかの``StateFn`` インスタンスが用意されています。例えば、Zero, One, Plus, Minus
です。
[11]:
from qiskit.aqua.operators import (StateFn, Zero, One, Plus, Minus, H,
DictStateFn, VectorStateFn, CircuitStateFn, OperatorStateFn)
Zero
と One
は量子状態 \(|0\rangle\) と \(|1\rangle\) を表します。これらは DictStateFn
で表されます。
[12]:
print(Zero, One)
DictStateFn({'0': 1}) DictStateFn({'1': 1})
Plus
と Minus``は、状態 :math:`(|0\rangle + |1\rangle)/\sqrt{2}` と :math:`(|0\rangle - |1\rangle)/\sqrt{2}` であり、回路を介して表されます。 ``H
は Plus
と同義です。
[13]:
print(Plus, Minus)
CircuitStateFn(
┌───┐
q_0: ┤ H ├
└───┘
) CircuitStateFn(
┌───┐┌───┐
q_0: ┤ X ├┤ H ├
└───┘└───┘
)
量子状態のインデックス作成は``eval`` メソッドで行われます。これらの例は``0`` と 1
基底状態の係数を返します。(以降、eval
メソッドが他の計算でも使用されることを示します。)
[14]:
print(Zero.eval('0'))
print(Zero.eval('1'))
print(One.eval('1'))
print(Plus.eval('0'))
print(Minus.eval('1'))
1.0
0.0
1.0
(0.7071067811865476+0j)
(-0.7071067811865476+8.7e-17j)
量子状態の双対ベクトル( ket に対応する bra ) は、adjoint
メソッドによって得られます。 StateFn
は is_measurement
フラグを持ちます。オブジェクトが ket の場合は False
で、ブラの場合は True
です。
ここでは \(\langle 1 |\) を作成します。
[15]:
One.adjoint()
[15]:
DictStateFn({'1': 1}, coeff=1.0, is_measurement=True)
便宜上、以下のように、チルダを使用して双対ベクトルを得ることができます。
[16]:
~One
[16]:
DictStateFn({'1': 1}, coeff=1.0, is_measurement=True)
代数演算と述語¶
StateFn
間の代数演算と述語が数多くサポートされています: * +
- 加算 * -
- 減算もしくは否定 (-1のスカラー乗算) * *
- スカラー乗算 * /
- スカラー除算 * @
- 合成 * ^
- テンソル積もしくはテンソル乗 (自己n倍テンソル) * **
- 合成乗 (自己n回合成) * ==
- 等式 * ~
- 随伴、状態関数と測定値の切り替え
演算子の優先順位 をオーバーライドするには、括弧が必要であることに注意してください。
StateFn
は係数を持ちます。これにより状態にスカラーを掛けることができ、和を構築することができます。
ここでは、\((2 + 3i)|0\rangle\) を構成します。
[17]:
(2.0 + 3.0j) * Zero
[17]:
DictStateFn({'0': 1}, coeff=(2+3j), is_measurement=False)
ここでは、2 つの DictStateFn
を足すと、同じ型のオブジェクトが返されることを見ましょう。 \(|0\rangle + |1\rangle\) を構築します。
[18]:
print(Zero + One)
DictStateFn({'0': 1.0, '1': 1.0})
状態を手動で正規化する必要があることに注意してください。例えば、\((|0\rangle + |1\rangle)/\sqrt{2}\) を構築するには、次のように記述します。
[19]:
import math
v_zero_one = (Zero + One) / math.sqrt(2)
print(v_zero_one)
DictStateFn({'0': 1.0, '1': 1.0}) * 0.7071067811865475
他の場合では、結果は和の記号表現になります。 例えば、次は \(|+\rangle + |-\rangle\) の表現です。
[20]:
print(Plus + Minus)
SummedOp([
CircuitStateFn(
┌───┐
q_0: ┤ H ├
└───┘
),
CircuitStateFn(
┌───┐┌───┐
q_0: ┤ X ├┤ H ├
└───┘└───┘
)
])
合成演算子は、デフォルトでは評価されていない形の内積を計算するのに使用します。次は \(\langle 1 | 1 \rangle\) の表現です。
[21]:
print(~One @ One)
ComposedOp([
DictMeasurement({'1': 1}),
DictStateFn({'1': 1})
])
is_measurement
フラグは、 (ブラ) 状態 ~One
に DictMeasurement
を表示させることに注意してください。
記号式は``eval`` メソッドで評価することができます。
[22]:
(~One @ One).eval()
[22]:
1.0
[23]:
(~v_zero_one @ v_zero_one).eval()
[23]:
0.9999999999999998
次は、\(\langle - | 1 \rangle = \langle (\langle 0| - \langle 1|)/\sqrt{2} | 1\rangle\) です。
[24]:
(~Minus @ One).eval()
[24]:
(-0.7071067811865476-8.7e-17j)
合成演算子 @
は、compose
メソッドを呼び出すのと同等です。
[25]:
print((~One).compose(One))
ComposedOp([
DictMeasurement({'1': 1}),
DictStateFn({'1': 1})
])
内積は、ComposedOp``を構築せずに、``eval
メソッドを直接使用して計算することもできます。
[26]:
(~One).eval(One)
[26]:
1.0
テンソル積の記号は次のように構築されます。次は \(|0\rangle \otimes |+\rangle\) です。
[27]:
print(Zero^Plus)
TensoredOp([
DictStateFn({'0': 1}),
CircuitStateFn(
┌───┐
q_0: ┤ H ├
└───┘
)
])
これは、シンプルな(複合していない)``CircuitStateFn`` として表現されています。
[28]:
print((Zero^Plus).to_circuit_op())
CircuitStateFn(
┌───┐
q_0: ┤ H ├
└───┘
q_1: ─────
)
テンソル乗はキャレット ^
を使用して、以下のように構築されます。次は、 \(600 (|11111\rangle + |00000\rangle)\) と \(|10\rangle^{\otimes 3}\) です。
[29]:
print(600 * ((One^5) + (Zero^5)))
print((One^Zero)^3)
DictStateFn({'11111': 1.0, '00000': 1.0}) * 600.0
DictStateFn({'101010': 1})
to_matrix_op
メソッドは VectorStateFn
に変換します。
[30]:
print(((Plus^Minus)^2).to_matrix_op())
print(((Plus^One)^2).to_circuit_op())
print(((Plus^One)^2).to_matrix_op().sample())
VectorStateFn(Statevector([ 0.25-6.1e-17j, -0.25+6.1e-17j, 0.25-6.1e-17j,
-0.25+6.1e-17j, -0.25+6.1e-17j, 0.25-6.1e-17j,
-0.25+6.1e-17j, 0.25-6.1e-17j, 0.25-6.1e-17j,
-0.25+6.1e-17j, 0.25-6.1e-17j, -0.25+6.1e-17j,
-0.25+6.1e-17j, 0.25-6.1e-17j, -0.25+6.1e-17j,
0.25-6.1e-17j],
dims=(2, 2, 2, 2)))
CircuitStateFn(
┌───┐
q_0: ┤ X ├
├───┤
q_1: ┤ H ├
├───┤
q_2: ┤ X ├
├───┤
q_3: ┤ H ├
└───┘
)
{'1101': 0.2626953125, '0111': 0.251953125, '1111': 0.2470703125, '0101': 0.23828125}
StateFnを構築するのは簡単です。 StateFn
クラスはファクトリーとしても機能し、該当するプリミティブをコンストラクターに取り込み、正しい StateFn サブクラスを返すことができます。 現在、以下のプリミティブをコンストラクタに渡すことができ、生成される StateFn
サブクラスと一緒にリストしています。
str (ある基底ビット文字列に等しい) -> DictStateFn
dict -> DictStateFn
QisKitのResultオブジェクト -> DictStateFn
list -> VectorStateFn
np.ndarray -> VectorStateFn
Statevector -> VectorStateFn
QuantumCircuit -> CircuitStateFn
Instruction -> CircuitStateFn
OperatorBase -> OperatorStateFn
[31]:
print(StateFn({'0':1}))
print(StateFn({'0':1}) == Zero)
print(StateFn([0,1,1,0]))
from qiskit.circuit.library import RealAmplitudes
print(StateFn(RealAmplitudes(2)))
DictStateFn({'0': 1})
True
VectorStateFn(Statevector([0.+0.j, 1.+0.j, 1.+0.j, 0.+0.j],
dims=(2, 2)))
CircuitStateFn(
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
q_0: ┤ RY(θ[0]) ├──■──┤ RY(θ[2]) ├──■──┤ RY(θ[4]) ├──■──┤ RY(θ[6]) ├
├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤
q_1: ┤ RY(θ[1]) ├┤ X ├┤ RY(θ[3]) ├┤ X ├┤ RY(θ[5]) ├┤ X ├┤ RY(θ[7]) ├
└──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘
)
パート II: PrimitiveOp
¶
基本的な演算子は PrimitiveOp
のサブクラスです。 StateFn``と同じように、``PrimitiveOp``は与えられたプリミティブに対して正しい型の``PrimitiveOp``を作成するためのファクトリーでもあります。 現在、以下のプリミティブをコンストラクタに渡すことができ、生成される ``PrimitiveOp
サブクラスと一緒にリストしています。
TerraのPauli -> PauliOp
Instruction -> CircuitOp
QuantumCircuit -> CircuitOp
2d List -> MatrixOp
np.ndarray -> MatrixOp
spmatrix -> MatrixOp
Terra の quantum_info.Operator -> MatrixOp
[32]:
from qiskit.aqua.operators import X, Y, Z, I, CX, T, H, S, PrimitiveOp
行列要素¶
eval
メソッドは演算子から列を返します。たとえば、パウリ \(X\) 演算子は PauliOp
によって表現されます。 列を要求すると、疎表現のインスタンス、DictStateFn
を返します。
[33]:
X
[33]:
PauliOp(Pauli(z=[False], x=[True]), coeff=1.0)
[34]:
print(X.eval('0'))
DictStateFn({'1': (1+0j)})
演算子のインデックス生成、すなわち行列要素の取得は、2回``eval`` メソッドを呼び出すことで実行されます。
\(X = \left(\begin{matrix} 0 & 1 \\ 1 & 0 \end{matrix} \right)\) であるので、行列要素 \(\left\{X \right\}_{0,1}\) は、以下の通りです。
[35]:
X.eval('0').eval('1')
[35]:
(1+0j)
回路で示したように、2量子ビット演算子 CX
、制御``X`` を使用した例は以下の通りです。
[36]:
print(CX)
print(CX.to_matrix().real) # The imaginary part vanishes.
q_0: ──■──
┌─┴─┐
q_1: ┤ X ├
└───┘
[[1. 0. 0. 0.]
[0. 0. 0. 1.]
[0. 0. 1. 0.]
[0. 1. 0. 0.]]
[37]:
CX.eval('01') # 01 is the one in decimal. We get the first column.
[37]:
VectorStateFn(Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
dims=(2, 2)), coeff=1.0, is_measurement=False)
[38]:
CX.eval('01').eval('11') # This returns element with (zero-based) index (1, 3)
[38]:
(1+0j)
状態ベクトルに演算子を適用する¶
状態ベクトルに演算子を適用するには、 compose``メソッド ( ``@
演算子と同等)を使用します。\(X | 1 \rangle = |0\rangle\) は次のように表されます。
[39]:
print(X @ One)
ComposedOp([
X,
DictStateFn({'1': 1})
])
:math:`|0rangle`のよりシンプルな表現、``DictStateFn``表現は``eval``で取得できます。
[40]:
(X @ One).eval()
[40]:
DictStateFn({'0': (1+0j)}, coeff=1.0, is_measurement=False)
eval
を直接使用することで、中間の ComposedOp
ステップを回避することができます。
[41]:
X.eval(One)
[41]:
DictStateFn({'0': (1+0j)}, coeff=1.0, is_measurement=False)
演算子の合成とテンソル積は @
と ^
で表現されます。以下にいくつかの例を示します。
[42]:
print(((~One^2) @ (CX.eval('01'))).eval())
print(((H^5) @ ((CX^2)^I) @ (I^(CX^2)))**2)
print((((H^5) @ ((CX^2)^I) @ (I^(CX^2)))**2) @ (Minus^5))
print(((H^I^I)@(X^I^I)@Zero))
(1+0j)
┌───┐┌───┐ ┌───┐┌───┐
q_0: ──■──┤ I ├┤ H ├──■──┤ I ├┤ H ├
┌─┴─┐└───┘├───┤┌─┴─┐└───┘├───┤
q_1: ┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├
└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤
q_2: ──■──┤ X ├┤ H ├──■──┤ X ├┤ H ├
┌─┴─┐└───┘├───┤┌─┴─┐└───┘├───┤
q_3: ┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├
├───┤┌─┴─┐├───┤├───┤┌─┴─┐├───┤
q_4: ┤ I ├┤ X ├┤ H ├┤ I ├┤ X ├┤ H ├
└───┘└───┘└───┘└───┘└───┘└───┘
CircuitStateFn(
┌───┐┌───┐ ┌───┐ ┌───┐
q_0: ┤ X ├┤ H ├──■──┤ H ├───────■──┤ H ├─────
├───┤├───┤┌─┴─┐└───┘┌───┐┌─┴─┐└───┘┌───┐
q_1: ┤ X ├┤ H ├┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├
├───┤├───┤└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤
q_2: ┤ X ├┤ H ├──■──┤ X ├┤ H ├──■──┤ X ├┤ H ├
├───┤├───┤┌─┴─┐└───┘├───┤┌─┴─┐└───┘├───┤
q_3: ┤ X ├┤ H ├┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├
├───┤├───┤└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤
q_4: ┤ X ├┤ H ├─────┤ X ├┤ H ├─────┤ X ├┤ H ├
└───┘└───┘ └───┘└───┘ └───┘└───┘
)
CircuitStateFn(
q_0: ──────────
q_1: ──────────
┌───┐┌───┐
q_2: ┤ X ├┤ H ├
└───┘└───┘
)
[43]:
print(~One @ Minus)
ComposedOp([
DictMeasurement({'1': 1}),
CircuitStateFn(
┌───┐┌───┐
q_0: ┤ X ├┤ H ├
└───┘└───┘
)
])
パート III ListOp
とサブクラス¶
ListOp
¶
ListOp
は、演算子と状態のリストに対し、効果的に演算をベクトル化するためのコンテナです。
[44]:
from qiskit.aqua.operators import ListOp
print((~ListOp([One, Zero]) @ ListOp([One, Zero])))
ComposedOp([
ListOp([
DictMeasurement({'1': 1}),
DictMeasurement({'0': 1})
]),
ListOp([
DictStateFn({'1': 1}),
DictStateFn({'0': 1})
])
])
例えば、上記の合成は簡約化メソッド``reduce``を使用してリスト(ListOp
) に分配されます。
[45]:
print((~ListOp([One, Zero]) @ ListOp([One, Zero])).reduce())
ListOp([
ListOp([
ComposedOp([
DictMeasurement({'1': 1}),
DictStateFn({'1': 1})
]),
ComposedOp([
DictMeasurement({'1': 1}),
DictStateFn({'0': 1})
])
]),
ListOp([
ComposedOp([
DictMeasurement({'0': 1}),
DictStateFn({'1': 1})
]),
ComposedOp([
DictMeasurement({'0': 1}),
DictStateFn({'0': 1})
])
])
])
ListOp
: SummedOp
, ComposedOp
, TensoredOp
¶
上記で紹介した ListOp
はベクトル化演算に役立ちますが、リストのような複合クラスのスーパークラスとしても機能します。 すでに上記の演算を行っている場合、 CircuitOp
の加算など、一般的に効率的に実行する方法がわからない``OperatorBase`` の演算を簡単にできることに気づくでしょう (あるいはまだ効率的な手順を実装していないだけです)。そのような場合、演算の遅延実行を表す演算から ListOp
の結果 (またはそれらのサブクラス) を受け取ることができます。 例えば、DictStateFn
と CircuitStateFn
を加算しようとした場合、2つの合計を表す SummedOp
を受け取ります。 この 合成状態関数は eval
が作用できます (ただし、両方をベクトルに変換するなど、内部でスケーラブルでない計算を実行する必要があるかもしれません) 。
これらの合成 OperatorBase
は、PrimitiveOp
と StateFn
の構成要素から、ますます複雑で豊富な計算を構築する方法です。
すべての ListOp
には以下の 4 つのプロパティがあります: * oplist
- 項、因数などを表すことができる OperatorBase
のリスト。 * combo_fn
- 複素数のリストを出力値として、oplist
の出力の組み合わせ方法を定義する関数。 簡便化のため、この関数は Numpy 配列で定義されています。 * coeff
- プリミティブに掛ける係数。 coeff
は、int、float、complex または my_op.bind_parameters``を使ってあとからバインドする(Terra の ``qiskit.circuit
の) Parameter
オブジェクトを取り得ることに注意してください。 * abelian
- oplist
のOperatorが相互に可換と知られているかどうかを示します (通常は AbelianGrouper
コンバーターによって変換された後に設定されます)。
ListOp
は、典型的なイテレーターのオーバーロードをサポートしているので、my_op[4]
のように添字を使って、``oplist``中の``OperatorBase``にアクセスすることができます。
OperatorStateFn
¶
前述のように OperatorStateFn
は密度演算子を表していますが、is_measurement
フラグが True
の場合、OperatorStateFn
は 観測量を表します。 この観測量の期待値は ComposedOp
を用いて構築することもできます。または、直接 eval
を使用して構築することもできます。 is_measurement
フラグ(プロパティ) が adjoint
メソッドで設定されることを思い出してください。
ここでは、 パウリ \(Z\) 演算子に対応する観測量を構築します。 出力時には、OperatorMeasurement
と呼ばれることに注意してください。
[46]:
print(StateFn(Z).adjoint())
StateFn(Z).adjoint()
OperatorMeasurement(Z)
[46]:
OperatorStateFn(PauliOp(Pauli(z=[True], x=[False]), coeff=1.0), coeff=1.0, is_measurement=True)
ここでは、\(\langle 0 | Z | 0 \rangle\)langle 1 | Z | 1 rangle`、\(\langle + | Z | + \rangle\) を計算します。ここで、\(|+\rangle = (|0\rangle + |1\rangle)/\sqrt{2}\) です。
[47]:
print(StateFn(Z).adjoint().eval(Zero))
print(StateFn(Z).adjoint().eval(One))
print(StateFn(Z).adjoint().eval(Plus))
(1+0j)
(-1+0j)
0j
パートIV: コンバーター¶
コンバーターは、演算子と状態を操作し、アルゴリズムの構成要素を実行するクラスです。例えば、演算子とトロッター化の基底を変更することなどがあります。 コンバーターは式をトラバースし、演算子内のコンバーターの convert()
メソッドによって定義された特定の操作または置き換えを実行します。 通常、コンバーターが 変換目的とは関係のない再帰中に`OperatorBase` に遭遇した場合、OperatorBase
は変更されません。
[48]:
import numpy as np
from qiskit.aqua.operators import I, X, Y, Z, H, CX, Zero, ListOp, PauliExpectation, PauliTrotterEvolution, CircuitSampler, MatrixEvolution, Suzuki
from qiskit.circuit import Parameter
from qiskit import BasicAer
時間発展と``exp_i()``、EvolvedOp
¶
全ての``PrimitiveOp`` と ListOp
は、H.exp_i()
が \(e^{-iH}\).exp_i()`` 関数を持ちます。 実際には、効率的に計算可能な指数を持つ演算子は数個しかありません (単位行列ではない単一量子ビットのパウリのみを持つMatrixOp や PauliOpsのように)。 ですから、プレースホルダーや記号表現を返す必要があります (加算できない場合 SummedOp
がプレースホルダーであることに似ています)。 このプレースホルダーは EvolvedOp
と呼ばれ、.primitive
プロパティで指数化される OperatorBase
を保持します。
Qiskit 演算子はパラメーター化を完全にサポートしているため、ここでは発展時間に Parameter
を使用できます。 どの関数にも「発展時間」引数がないことに注意してください。 どのような演算子でも、指数パラメーターに反映される発展時間 \(e^{-iHt}\) を演算子にかけることを選択した場合、Operator flowは指数関数に取ります。
パウリ演算子の加重和¶
多量子ビットパウリ演算子の線型結合として表現されるハミルトニアンは、次のように構築することができます。
[49]:
two_qubit_H2 = (-1.0523732 * I^I) + \
(0.39793742 * I^Z) + \
(-0.3979374 * Z^I) + \
(-0.0112801 * Z^Z) + \
(0.18093119 * X^X)
ここで、two_qubit_H2
は、項が PauliOp
である SummedOp
として表現されます。
[50]:
print(two_qubit_H2)
SummedOp([
-1.0523732 * II,
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ,
0.18093119 * XX
])
次に、ハミルトニアンに``Parameter`` を掛けます。この Parameter
は SummedOp
の coeff
プロパティに保存されています。 結果に対し exp_i()
を呼び出すと、指数化を表す EvolvedOp
でラップされます。
[51]:
evo_time = Parameter('θ')
evolution_op = (evo_time*two_qubit_H2).exp_i()
print(evolution_op) # Note, EvolvedOps print as exponentiations
print(repr(evolution_op))
e^(-i*1.0*θ * SummedOp([
-1.0523732 * II,
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ,
0.18093119 * XX
]))
EvolvedOp(SummedOp([PauliOp(Pauli(z=[False, False], x=[False, False]), coeff=-1.0523732), PauliOp(Pauli(z=[True, False], x=[False, False]), coeff=0.39793742), PauliOp(Pauli(z=[False, True], x=[False, False]), coeff=-0.3979374), PauliOp(Pauli(z=[True, True], x=[False, False]), coeff=-0.0112801), PauliOp(Pauli(z=[False, False], x=[True, True]), coeff=0.18093119)], coeff=1.0*θ, abelian=False), coeff=1.0)
two_qubit_H2
の観測量として、h2_measurement
を構築しましょう。
[52]:
h2_measurement = StateFn(two_qubit_H2).adjoint()
print(h2_measurement)
OperatorMeasurement(SummedOp([
-1.0523732 * II,
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ,
0.18093119 * XX
]))
\(\text{CX} (H\otimes I) |00\rangle\) を用いて、ベル状態 \(|\Phi_+\rangle\) を構築します。
[53]:
bell = CX @ (I ^ H) @ Zero
print(bell)
CircuitStateFn(
┌───┐
q_0: ┤ H ├──■──
└───┘┌─┴─┐
q_1: ─────┤ X ├
└───┘
)
次は、 \(H e^{-iHt} |\Phi_+\rangle\) の式です。
[54]:
evo_and_meas = h2_measurement @ evolution_op @ bell
print(evo_and_meas)
ComposedOp([
OperatorMeasurement(SummedOp([
-1.0523732 * II,
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ,
0.18093119 * XX
])),
e^(-i*1.0*θ * SummedOp([
-1.0523732 * II,
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ,
0.18093119 * XX
])),
CircuitStateFn(
┌───┐
q_0: ┤ H ├──■──
└───┘┌─┴─┐
q_1: ─────┤ X ├
└───┘
)
])
通常、2量子ビットゲートを使用して、 \(e^{-iHt}\) を近似します。 これは 、出現する全ての``EvolvedOp``をトロッター化した式をトラバースする``PauliTrotterEvolution`` の convert
メソッドによって達成されます。 ここでは PauliTrotterEvolution
を使用していますが、他にも正確に指数化を実行する MatrixEvolution
のような可能性があります。
[55]:
trotterized_op = PauliTrotterEvolution(trotter_mode=Suzuki(order=2, reps=1)).convert(evo_and_meas)
# We can also set trotter_mode='suzuki' or leave it empty to default to first order Trotterization.
print(trotterized_op)
ComposedOp([
OperatorMeasurement(SummedOp([
-1.0523732 * II,
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ,
0.18093119 * XX
])),
CircuitStateFn(
global phase: 1.0524
┌───┐ ┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐»
q_0: ┤ H ├──■──┤ H ├┤ X ├┤ RZ(0.18093119*θ) ├┤ X ├┤ H ├┤ X ├»
└───┘┌─┴─┐├───┤└─┬─┘└──────────────────┘└─┬─┘├───┤└─┬─┘»
q_1: ─────┤ X ├┤ H ├──■────────────────────────■──┤ H ├──■──»
└───┘└───┘ └───┘ »
« ┌──────────────────┐┌───┐┌──────────────────┐┌──────────────────┐┌───┐»
«q_0: ┤ RZ(-0.0112801*θ) ├┤ X ├┤ RZ(0.39793742*θ) ├┤ RZ(0.39793742*θ) ├┤ X ├»
« └──────────────────┘└─┬─┘├──────────────────┤├──────────────────┤└─┬─┘»
«q_1: ──────────────────────■──┤ RZ(-0.3979374*θ) ├┤ RZ(-0.3979374*θ) ├──■──»
« └──────────────────┘└──────────────────┘ »
« ┌──────────────────┐┌───┐┌───┐┌───┐┌──────────────────┐┌───┐┌───┐
«q_0: ┤ RZ(-0.0112801*θ) ├┤ X ├┤ H ├┤ X ├┤ RZ(0.18093119*θ) ├┤ X ├┤ H ├
« └──────────────────┘└─┬─┘├───┤└─┬─┘└──────────────────┘└─┬─┘├───┤
«q_1: ──────────────────────■──┤ H ├──■────────────────────────■──┤ H ├
« └───┘ └───┘
)
])
trotterized_op
は Parameter
を含んでいます。bind_parameters
メソッドは、dict
で指定されたパラメータ名をバインドする式をトラバースします。 この場合、パラメータは一つしかありません。
[56]:
bound = trotterized_op.bind_parameters({evo_time: .5})
bound
は ComposedOp
です。2番目の因子は回路です。バインディングが行われたことを確認するために、描画してみましょう。
[57]:
bound[1].to_circuit().draw()
[57]:
global phase: 1.0524 ┌───┐ ┌───┐┌───┐┌────────────────────────┐┌───┐┌───┐┌───┐» q_0: ┤ H ├──■──┤ H ├┤ X ├┤ RZ(0.0904655950000000) ├┤ X ├┤ H ├┤ X ├» └───┘┌─┴─┐├───┤└─┬─┘└────────────────────────┘└─┬─┘├───┤└─┬─┘» q_1: ─────┤ X ├┤ H ├──■──────────────────────────────■──┤ H ├──■──» └───┘└───┘ └───┘ » « ┌──────────────────────────┐┌───┐┌───────────────────────┐ » «q_0: ┤ RZ(-0.00564005000000000) ├┤ X ├┤ RZ(0.198968710000000) ├─» « └──────────────────────────┘└─┬─┘├───────────────────────┴┐» «q_1: ──────────────────────────────■──┤ RZ(-0.198968700000000) ├» « └────────────────────────┘» « ┌───────────────────────┐ ┌───┐┌──────────────────────────┐┌───┐┌───┐» «q_0: ┤ RZ(0.198968710000000) ├─┤ X ├┤ RZ(-0.00564005000000000) ├┤ X ├┤ H ├» « ├───────────────────────┴┐└─┬─┘└──────────────────────────┘└─┬─┘├───┤» «q_1: ┤ RZ(-0.198968700000000) ├──■────────────────────────────────■──┤ H ├» « └────────────────────────┘ └───┘» « ┌───┐┌────────────────────────┐┌───┐┌───┐ «q_0: ┤ X ├┤ RZ(0.0904655950000000) ├┤ X ├┤ H ├ « └─┬─┘└────────────────────────┘└─┬─┘├───┤ «q_1: ──■──────────────────────────────■──┤ H ├ « └───┘
期待値¶
Expectation
は、観測量の期待値の計算を可能にするコンバーターです。 これらは OperatorStateFn
(観測量) を、量子ハードウェアや古典ハードウェア上の計算により適している等価な命令に置き換えるよう、Operator構造をトラバースします。 例えば、 ある状態関数に対するパウリ行列の合計として表現されたOperator o
の期待値を測定したいけれど、量子ハードウェア上の対角化された測定にしかアクセスできない場合、 観測可能な``~StateFn(o)`` を作成し``PauliExpectation`` を使用して、対角化測定と状態に追加する回路のプリローテーションに変換します。
興味深いもう一つの Expectation
は AerPauliExpectation
です。 これは、期待値を、Aer
が高性能でネイティブに実行できる特別な期待値スナップショットの命令を含む CircuitStateFn
に変換します。
[58]:
# Note that XX was the only non-diagonal measurement in our H2 Observable
print(PauliExpectation(group_paulis=False).convert(h2_measurement))
SummedOp([
ComposedOp([
OperatorMeasurement(-1.0523732 * II),
II
]),
ComposedOp([
OperatorMeasurement(0.39793742 * IZ),
II
]),
ComposedOp([
OperatorMeasurement(-0.3979374 * ZI),
II
]),
ComposedOp([
OperatorMeasurement(-0.0112801 * ZZ),
II
]),
ComposedOp([
OperatorMeasurement(0.18093119 * ZZ),
┌───┐
q_0: ┤ H ├
├───┤
q_1: ┤ H ├
└───┘
])
])
デフォルトの``group_paulis=True``では、 AbrianGrouper
を使用して SummedOp
を互いに量子的に可換なパウリのグループに変換します。 これにより、各グループが同じ回路実行を共有できるため、回路の実行オーバーヘッドが低減されます。
[59]:
print(PauliExpectation().convert(h2_measurement))
SummedOp([
ComposedOp([
OperatorMeasurement(AbelianSummedOp([
-1.0523732 * II,
0.18093119 * ZZ
])),
┌───┐
q_0: ┤ H ├
├───┤
q_1: ┤ H ├
└───┘
]),
ComposedOp([
OperatorMeasurement(AbelianSummedOp([
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ
])),
II
])
])
コンバーターは再帰的に動作することに注意してください。つまり、可能な限りアクションを適用する式にトラバースします。 時間発展と測定の完全な表現を変換することができます。 変換された h2_measurement
を 時間発展``CircuitStateFn`` と同等に構成することができます。式全体を変換して進めます。
[60]:
diagonalized_meas_op = PauliExpectation().convert(trotterized_op)
print(diagonalized_meas_op)
SummedOp([
ComposedOp([
OperatorMeasurement(AbelianSummedOp([
-1.0523732 * II,
0.18093119 * ZZ
])),
CircuitStateFn(
global phase: 1.0524
┌───┐ ┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐»
q_0: ┤ H ├──■──┤ H ├┤ X ├┤ RZ(0.18093119*θ) ├┤ X ├┤ H ├┤ X ├»
└───┘┌─┴─┐├───┤└─┬─┘└──────────────────┘└─┬─┘├───┤└─┬─┘»
q_1: ─────┤ X ├┤ H ├──■────────────────────────■──┤ H ├──■──»
└───┘└───┘ └───┘ »
« ┌──────────────────┐┌───┐┌──────────────────┐┌──────────────────┐┌───┐»
«q_0: ┤ RZ(-0.0112801*θ) ├┤ X ├┤ RZ(0.39793742*θ) ├┤ RZ(0.39793742*θ) ├┤ X ├»
« └──────────────────┘└─┬─┘├──────────────────┤├──────────────────┤└─┬─┘»
«q_1: ──────────────────────■──┤ RZ(-0.3979374*θ) ├┤ RZ(-0.3979374*θ) ├──■──»
« └──────────────────┘└──────────────────┘ »
« ┌──────────────────┐┌───┐┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐
«q_0: ┤ RZ(-0.0112801*θ) ├┤ X ├┤ H ├┤ X ├┤ RZ(0.18093119*θ) ├┤ X ├┤ H ├┤ H ├
« └──────────────────┘└─┬─┘├───┤└─┬─┘└──────────────────┘└─┬─┘├───┤├───┤
«q_1: ──────────────────────■──┤ H ├──■────────────────────────■──┤ H ├┤ H ├
« └───┘ └───┘└───┘
)
]),
ComposedOp([
OperatorMeasurement(AbelianSummedOp([
0.39793742 * IZ,
-0.3979374 * ZI,
-0.0112801 * ZZ
])),
CircuitStateFn(
global phase: 1.0524
┌───┐ ┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐»
q_0: ┤ H ├──■──┤ H ├┤ X ├┤ RZ(0.18093119*θ) ├┤ X ├┤ H ├┤ X ├»
└───┘┌─┴─┐├───┤└─┬─┘└──────────────────┘└─┬─┘├───┤└─┬─┘»
q_1: ─────┤ X ├┤ H ├──■────────────────────────■──┤ H ├──■──»
└───┘└───┘ └───┘ »
« ┌──────────────────┐┌───┐┌──────────────────┐┌──────────────────┐┌───┐»
«q_0: ┤ RZ(-0.0112801*θ) ├┤ X ├┤ RZ(0.39793742*θ) ├┤ RZ(0.39793742*θ) ├┤ X ├»
« └──────────────────┘└─┬─┘├──────────────────┤├──────────────────┤└─┬─┘»
«q_1: ──────────────────────■──┤ RZ(-0.3979374*θ) ├┤ RZ(-0.3979374*θ) ├──■──»
« └──────────────────┘└──────────────────┘ »
« ┌──────────────────┐┌───┐┌───┐┌───┐┌──────────────────┐┌───┐┌───┐
«q_0: ┤ RZ(-0.0112801*θ) ├┤ X ├┤ H ├┤ X ├┤ RZ(0.18093119*θ) ├┤ X ├┤ H ├
« └──────────────────┘└─┬─┘├───┤└─┬─┘└──────────────────┘└─┬─┘├───┤
«q_1: ──────────────────────■──┤ H ├──■────────────────────────■──┤ H ├
« └───┘ └───┘
)
])
])
次に、複数のパラメータ値を ListOp
にバインドし、式全体を評価するために eval
を追加します。 先にバインドすれば eval
を使えたかもしれませんが、効率的ではありません。 ここで、eval
は内部シミュレーションを通じて``CircuitStateFn``を``VectorStateFn``に変換します。
[61]:
evo_time_points = list(range(8))
h2_trotter_expectations = diagonalized_meas_op.bind_parameters({evo_time: evo_time_points})
パラメータの異なる値に対応する \(\langle \Phi_+| e^{iHt} H e^{-iHt} |\Phi_+\rangle\) の期待値は次のとおりです。
[62]:
h2_trotter_expectations.eval()
[62]:
array([-0.88272211-1.111e-15j, -0.88272211-1.165e-15j,
-0.88272211-1.065e-15j, -0.88272211-1.178e-15j,
-0.88272211-1.113e-15j, -0.88272211-9.250e-16j,
-0.88272211-1.054e-15j, -0.88272211-1.156e-15j])
CircuitSampler
を用いて CircuitStateFn
を実行する¶
CircuitSampler
はOperatorをトラバースし、CircuitStateFns
を量子バックエンドを使用して、DictStateFn
または VectorStateFn
により結果の状態関数の近似に変換します。 CircuitStateFn
の値を近似するには、1) 全ての位相情報を破壊する脱分極チャネルを介して状態関数を送信、2) サンプルした周波数を、サンプリングした生の確率 ではなく、周波数の 平方根 に置き換える(Bornの規則により、状態関数の 平方 をサンプリングするのと同等です)、必要があることに注意してください。
[63]:
sampler = CircuitSampler(backend=BasicAer.get_backend('qasm_simulator'))
# sampler.quantum_instance.run_config.shots = 1000
sampled_trotter_exp_op = sampler.convert(h2_trotter_expectations)
sampled_trotter_energies = sampled_trotter_exp_op.eval()
print('Sampled Trotterized energies:\n {}'.format(np.real(sampled_trotter_energies)))
Sampled Trotterized energies:
[-0.88272211 -0.88272211 -0.88272211 -0.88272211 -0.88272211 -0.88272211
-0.88272211 -0.88272211]
回路は、回路のサンプリング確率の**平方根** のdictに置き換えられることに再度注意してください。 変換前と変換後のひとつの部分式を見てみましょう。
[64]:
print('Before:\n')
print(h2_trotter_expectations.reduce()[0][0])
print('\nAfter:\n')
print(sampled_trotter_exp_op[0][0])
Before:
ComposedOp([
OperatorMeasurement(AbelianSummedOp([
-1.0523732 * II,
0.18093119 * ZZ
])),
CircuitStateFn(
global phase: 1.0524
┌───┐ ┌───┐┌───┐┌───────┐┌───┐┌───┐┌───┐┌───────┐┌───┐┌───────┐»
q_0: ┤ H ├──■──┤ H ├┤ X ├┤ RZ(0) ├┤ X ├┤ H ├┤ X ├┤ RZ(0) ├┤ X ├┤ RZ(0) ├»
└───┘┌─┴─┐├───┤└─┬─┘└───────┘└─┬─┘├───┤└─┬─┘└───────┘└─┬─┘├───────┤»
q_1: ─────┤ X ├┤ H ├──■─────────────■──┤ H ├──■─────────────■──┤ RZ(0) ├»
└───┘└───┘ └───┘ └───────┘»
« ┌───────┐┌───┐┌───────┐┌───┐┌───┐┌───┐┌───────┐┌───┐┌───┐┌───┐
«q_0: ┤ RZ(0) ├┤ X ├┤ RZ(0) ├┤ X ├┤ H ├┤ X ├┤ RZ(0) ├┤ X ├┤ H ├┤ H ├
« ├───────┤└─┬─┘└───────┘└─┬─┘├───┤└─┬─┘└───────┘└─┬─┘├───┤├───┤
«q_1: ┤ RZ(0) ├──■─────────────■──┤ H ├──■─────────────■──┤ H ├┤ H ├
« └───────┘ └───┘ └───┘└───┘
)
])
After:
ComposedOp([
OperatorMeasurement(AbelianSummedOp([
-1.0523732 * II,
0.18093119 * ZZ
])),
DictStateFn({'00': 0.7207851621669248, '11': 0.6931585316505886})
])
[65]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.17.0.dev0+8c40b02 |
Aer | 0.5.2 |
Ignis | 0.4.0.dev0+15b7177 |
Aqua | 0.9.0.dev0+2734384 |
IBM Q Provider | 0.7.2 |
System information | |
Python | 3.8.3 (default, May 17 2020, 18:15:42) [GCC 10.1.0] |
OS | Linux |
CPUs | 12 |
Memory (Gb) | 62.77165603637695 |
Wed Nov 04 13:35:52 2020 EST |
This code is a part of Qiskit
© Copyright IBM 2017, 2020.
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.