Japanese
言語
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

注釈

このページは 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)

ZeroOne は量子状態 \(|0\rangle\)\(|1\rangle\) を表します。これらは DictStateFn で表されます。

[12]:
print(Zero, One)
DictStateFn({'0': 1}) DictStateFn({'1': 1})

PlusMinus は、状態 \((|0\rangle + |1\rangle)/\sqrt{2}\)\((|0\rangle - |1\rangle)/\sqrt{2}\) であり、回路を介して表されます。 HPlus と同義です。

[13]:
print(Plus, Minus)
CircuitStateFn(
   ┌───┐
q: ┤ H ├
   └───┘
) CircuitStateFn(
   ┌───┐┌───┐
q: ┤ X ├┤ H ├
   └───┘└───┘
)

量子状態のインデックス作成は eval メソッドで行われます。これらの例は 01 基底状態の係数を返します。(以降、 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 メソッドによって得られます。 StateFnis_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 フラグは、 (ブラ) 状態 ~OneDictMeasurement を表示させることに注意してください。

記号式は 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 の結果 (またはそれらのサブクラス) を受け取ることができます。 例えば、DictStateFnCircuitStateFn を加算しようとした場合、2つの合計を表す SummedOp を受け取ります。 この 合成状態関数は eval が作用できます (ただし、両方をベクトルに変換するなど、内部でスケーラブルでない計算を実行する必要があるかもしれません) 。

これらの合成 OperatorBase は、PrimitiveOpStateFn の構成要素から、ますます複雑で豊富な計算を構築する方法です。

ListOp には以下の 4 つのプロパティがあります。

  • oplist - 用語、因子などを表すことができる OperatorBase のリスト。

  • combo_fn - oplist アイテムの出力を組み合わせる方法を定義する、複素数のリストを出力値に取得する関数。簡単にブロードキャスティングするため、この関数は Numpy 配列で定義されています。

  • coeff - プリミティブを掛ける係数。 coeff は int, float, complex or free Parameter オブジェクト ( 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

全ての PrimitiveOpListOp は、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 を掛けます。この ParameterSummedOpcoeff プロパティに保存されています。 結果に対し 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 をトロッター化した式をトラバースする PauliTrotterEvolutionconvert メソッドによって達成されます。 ここでは 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_opParameter を含んでいます。bind_parameters メソッドは、dict で指定されたパラメータ名をバインドする式をトラバースします。 この場合、パラメータは一つしかありません。

[56]:
bound = trotterized_op.bind_parameters({evo_time: .5})

boundComposedOp です。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 を使用して、対角化測定と状態に追加する回路のプリローテーションに変換します。

興味深いもう一つの ExpectationAerPauliExpectation です。 これは、期待値を、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 は内部シミュレーションを通じて CircuitStateFnVectorStateFn に変換します。

[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 SoftwareVersion
qiskit-terra0.19.0.dev0+fe3eb3f
qiskit-aer0.10.0.dev0+b78f265
qiskit-ignis0.7.0.dev0+0eb9dcc
qiskit-ibmq-provider0.17.0.dev0+15d2dfe
System information
Python version3.9.5
Python compilerClang 10.0.0
Python builddefault, May 18 2021 12:31:01
OSDarwin
CPUs4
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.