注釈
このページは tutorials/operators/01_operator_flow.ipynb から生成されました。
Operator Flow¶
はじめに¶
Qiskit には、状態と演算子、合計、テンソル積、合成を表すクラスが用意されています。これらの代数的構成物により、演算子を表す式を構築することができます。
まずパウリ演算子を用いて構築する式を紹介します。 後続のセクションでは、より詳細な演算子と状態、それらがどのように表現されるか、そしてそれらを使って何ができるかについて説明します。 最後のセクションでは、状態を構築し、ハミルトニアンでそれを発展させ、観測可能な期待値を計算します。
パウリ演算子、和、合成、およびテンソル積¶
最も重要な基礎的演算子は、パウリ演算子です。パウリ演算子は、このように表現されます。
[1]:
from qiskit.opflow 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)
1.0 * X
+ 2.0 * Y
テンソル積は、次のようにキャレット(脱字記号) で表されます。
[4]:
print(X^Y^Z)
XYZ
合成は``@`` 記号で表されます。
[5]:
print(X @ Y @ Z)
iI
前述の2つの例では、テンソル積とパウリ演算子の合成は即時に(おそらく複数量子ビットの) 等価なパウリ演算子に減らされました。 テンソルまたはより複雑なオブジェクトを構成する場合、結果は評価されていない演算を表すオブジェクトになります。つまり、代数式です。
例えば、2つの和を合成すると:
[6]:
print((X + Y) @ (Y + Z))
1j * Z
+ -1j * Y
+ 1.0 * I
+ 1j * X
また、2つの和のテンソル積を取ると:
[7]:
print((X + Y) ^ (Y + Z))
1.0 * XY
+ 1.0 * XZ
+ 1.0 * YY
+ 1.0 * YZ
上記の種類を詳しく見てみましょう。まずはパウリ演算子です。
[8]:
(I, X)
[8]:
(PauliOp(Pauli('I'), coeff=1.0), PauliOp(Pauli('X'), coeff=1.0))
各パウリ演算子は PauliOp
のインスタンスで、qiskit.quantum_info.Pauli
のインスタンスをラップし、係数 coeff
を追加します。一般的に, PauliOp
はパウリ演算子の加重テンソル積を表します.
[9]:
2.0 * X^Y^Z
[9]:
PauliOp(Pauli('XYZ'), coeff=2.0)
パウリ演算子をブール値のペアとしてエンコーディングするには、qiskit.quantum_info.Pauli
のドキュメントを参照してください。
演算子を表現する全てのオブジェクトは、 PauliOp
のような「プリミティブ」でも代数式でも、係数を持ちます。
[10]:
print(1.1 * ((1.2 * X)^(Y + (1.3 * Z))))
1.2 * (
1.1 * XY
+ 1.4300000000000002 * XZ
)
以下では、Qiskitの演算子、状態、および量子アルゴリズムの構成要素をより幅広く詳しく見ていきます。
パートI: 状態関数と測定¶
量子状態は StateFn
クラスのサブクラスとして表されます。量子状態には4つの表現があります: DictStateFn
は、dict
が背後にある計算基底での疎な表現です。 VectorStateFn
はnumpy配列が背後にある計算規定での密な表現です。CircuitStateFn
は回路が背後にあり、全部ゼロである計算基底状態で回路を実行した時に得られる状態を表現します。OperatorStateFn
は密度行列に対する混合状態を表現します。(後述するように、OperatorStateFn
は観測量を表現するためにも使用されます。)
便宜上、いくつかの``StateFn`` インスタンスが用意されています。例えば、Zero, One, Plus, Minus
です。
[11]:
from qiskit.opflow 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
は、状態 \((|0\rangle + |1\rangle)/\sqrt{2}\) と \((|0\rangle - |1\rangle)/\sqrt{2}\) であり、回路を介して表されます。 H
は Plus
と同義です。
[13]:
print(Plus, Minus)
CircuitStateFn(
┌───┐
q: ┤ H ├
└───┘
) CircuitStateFn(
┌───┐┌───┐
q: ┤ 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.7071067811865475+0j)
(-0.7071067811865475+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)
代数演算と述語¶
以下を含む、 StateFns
間の多くの代数演算と述語がサポートされています。
+
- 加算-
- 減算、否定(-1によるスカラー乗算)*
- スカラー乗法/
- スカラー除算@
- 合成^
- テンソル積またはテンソルべき乗(自己n回のテンソル)**
- 合成べき乗(自己でn回合成)==
- 等式~
- 随伴、状態関数と測定を交互に繰り返す
これらの演算子は、 演算子の優先順位に関するPythonの規則 に従うことに十分注意してください。これは、数学的に期待するものではない可能性があります。 たとえば、Pythonは
^
の前に +
を評価するため、 I^X + X^I
は実際には I ^ (X + X) ^ I == 2 * (I^X^I)
として解析されます。 このような場合は、メソッド( .tensor()
など)または括弧を使用できます。
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: ┤ H ├
└───┘
),
CircuitStateFn(
┌───┐┌───┐
q: ┤ 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.7071067811865475-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: ┤ 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.2734375, '0111': 0.2509765625, '1111': 0.24609375, '0101': 0.2294921875}
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: ┤0 ├
│ RealAmplitudes(θ[0],θ[1],θ[2],θ[3],θ[4],θ[5],θ[6],θ[7]) │
q_1: ┤1 ├
└──────────────────────────────────────────────────────────┘
)
パート 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.opflow import X, Y, Z, I, CX, T, H, S, PrimitiveOp
行列要素¶
eval
メソッドは演算子から列を返します。たとえば、パウリ \(X\) 演算子は PauliOp
によって表現されます。 列を要求すると、疎表現のインスタンス、DictStateFn
を返します。
[33]:
X
[33]:
PauliOp(Pauli('X'), 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})
])
\(|0\rangle\) のよりシンプルな表現、 DictStateFn
表現は eval
で取得できます。
[40]:
(X @ One).eval()
[40]:
DictStateFn({'0': (1+0j)}, coeff=(1+0j), is_measurement=False)
eval
を直接使用することで、中間の ComposedOp
ステップを回避することができます。
[41]:
X.eval(One)
[41]:
DictStateFn({'0': (1+0j)}, coeff=(1+0j), 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: ──■──┤ H ├───────■──┤ H ├─────
┌─┴─┐└───┘┌───┐┌─┴─┐└───┘┌───┐
q_1: ┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├
└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤
q_2: ──■──┤ X ├┤ H ├──■──┤ X ├┤ H ├
┌─┴─┐└───┘├───┤┌─┴─┐└───┘├───┤
q_3: ┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├
└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤
q_4: ─────┤ X ├┤ H ├─────┤ 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: ┤0 ├─────
│ │
q_1: ┤1 Pauli(XII) ├─────
│ │┌───┐
q_2: ┤2 ├┤ H ├
└─────────────┘└───┘
)
[43]:
print(~One @ Minus)
ComposedOp([
DictMeasurement({'1': 1}),
CircuitStateFn(
┌───┐┌───┐
q: ┤ X ├┤ H ├
└───┘└───┘
)
])
パート III: ListOp
とサブクラス¶
ListOp
¶
ListOp
は、演算子と状態のリストに対し、効果的に演算をベクトル化するためのコンテナです。
[44]:
from qiskit.opflow 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 or freeParameter
オブジェクト (qiskit.circuit
から) になることに注意してください。my_op.bind_parameters
を使って後にバインドされます。abelian
-oplist
の演算子が相互変換することが知られているかどうかを示します(通常はAbrianGrouper
コンバータによって変換された後に設定されます)。
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'), 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.opflow import I, X, Y, Z, H, CX, Zero, ListOp, PauliExpectation, PauliTrotterEvolution, CircuitSampler, MatrixEvolution, Suzuki
from qiskit.circuit import Parameter
from qiskit import Aer
時間発展と 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)
-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*θ * (
-1.0523732 * II
+ 0.39793742 * IZ
- 0.3979374 * ZI
- 0.0112801 * ZZ
+ 0.18093119 * XX
))
EvolvedOp(PauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'],
coeffs=[-1.0523732 +0.j, 0.39793742+0.j, -0.3979374 +0.j, -0.0112801 +0.j,
0.18093119+0.j]), coeff=1.0*θ), coeff=1.0)
two_qubit_H2
の観測量として、h2_measurement
を構築しましょう。
[52]:
h2_measurement = StateFn(two_qubit_H2).adjoint()
print(h2_measurement)
OperatorMeasurement(-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(-1.0523732 * II
+ 0.39793742 * IZ
- 0.3979374 * ZI
- 0.0112801 * ZZ
+ 0.18093119 * XX),
e^(-i*1.0*θ * (
-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(-1.0523732 * II
+ 0.39793742 * IZ
- 0.3979374 * ZI
- 0.0112801 * ZZ
+ 0.18093119 * XX),
CircuitStateFn(
global phase: 1.0523732*θ
┌───┐ ┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐»
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: 0.52619 ┌───┐ ┌───┐┌───┐┌─────────────────┐┌───┐┌───┐┌───┐┌─────────────────┐» q_0: ┤ H ├──■──┤ H ├┤ X ├┤ Rz(0.090465595) ├┤ X ├┤ H ├┤ X ├┤ Rz(-0.00564005) ├» └───┘┌─┴─┐├───┤└─┬─┘└─────────────────┘└─┬─┘├───┤└─┬─┘└─────────────────┘» q_1: ─────┤ X ├┤ H ├──■───────────────────────■──┤ H ├──■─────────────────────» └───┘└───┘ └───┘ » « ┌───┐┌────────────────┐┌────────────────┐┌───┐┌─────────────────┐┌───┐» «q_0: ┤ X ├┤ Rz(0.19896871) ├┤ Rz(0.19896871) ├┤ X ├┤ Rz(-0.00564005) ├┤ X ├» « └─┬─┘├────────────────┤├────────────────┤└─┬─┘└─────────────────┘└─┬─┘» «q_1: ──■──┤ Rz(-0.1989687) ├┤ Rz(-0.1989687) ├──■───────────────────────■──» « └────────────────┘└────────────────┘ » « ┌───┐┌───┐┌─────────────────┐┌───┐┌───┐ «q_0: ┤ H ├┤ X ├┤ Rz(0.090465595) ├┤ X ├┤ H ├ « ├───┤└─┬─┘└─────────────────┘└─┬─┘├───┤ «q_1: ┤ H ├──■───────────────────────■──┤ 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(0.18093119 * ZZ
- 1.0523732 * II),
┌───┐
q_0: ┤ H ├
├───┤
q_1: ┤ H ├
└───┘
]),
ComposedOp([
OperatorMeasurement(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(0.18093119 * ZZ
- 1.0523732 * II),
CircuitStateFn(
global phase: 1.0523732*θ
┌───┐ ┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐»
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(0.39793742 * IZ
- 0.3979374 * ZI
- 0.0112801 * ZZ),
CircuitStateFn(
global phase: 1.0523732*θ
┌───┐ ┌───┐┌───┐┌──────────────────┐┌───┐┌───┐┌───┐»
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+0.0e+00j, -0.88272211+0.0e+00j, -0.88272211+0.0e+00j,
-0.88272211+0.0e+00j, -0.88272211+0.0e+00j, -0.88272211+0.0e+00j,
-0.88272211+5.6e-17j, -0.88272211+0.0e+00j])
CircuitSampler
を用いて CircuitStateFn
を実行する¶
CircuitSampler
はOperatorをトラバースし、CircuitStateFns
を量子バックエンドを使用して、DictStateFn
または VectorStateFn
により結果の状態関数の近似に変換します。 CircuitStateFn
の値を近似するには、1) 全ての位相情報を破壊するデポラライズチャネルを介して状態関数を送信、2) サンプルした周波数を、サンプリングした生の確率 ではなく、周波数の 平方根 に置き換える(Bornルールにより、状態関数の 平方 をサンプリングするのと同等です)、必要があることに注意してください。
[63]:
sampler = CircuitSampler(backend=Aer.get_backend('aer_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(0.18093119 * ZZ
- 1.0523732 * II),
CircuitStateFn(
┌───┐ ┌───┐┌───┐┌───────┐┌───┐┌───┐┌───┐┌───────┐┌───┐┌───────┐»
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(0.18093119 * ZZ
- 1.0523732 * II),
DictStateFn({'00': 0.7167090588237321, '11': 0.6973723001381686})
])
[65]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.19.0.dev0+fe3eb3f |
qiskit-aer | 0.10.0.dev0+b78f265 |
qiskit-ignis | 0.7.0.dev0+0eb9dcc |
qiskit-ibmq-provider | 0.17.0.dev0+15d2dfe |
System information | |
Python version | 3.9.5 |
Python compiler | Clang 10.0.0 |
Python build | default, May 18 2021 12:31:01 |
OS | Darwin |
CPUs | 4 |
Memory (Gb) | 32.0 |
Fri Oct 29 16:10:45 2021 BST |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
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.