注釈

当ページは tutorials/circuits_advanced/2_operators_overview.ipynb から生成されました。

オペレーター

[7]:
import numpy as np

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import execute, BasicAer
from qiskit.compiler import transpile
from qiskit.quantum_info.operators import Operator, Pauli
from qiskit.quantum_info import process_fidelity

from qiskit.extensions import RXGate, XGate, CXGate

Operator クラス

Operator クラスは、量子系に作用する行列演算子を表すために Qiskit で利用されています。小さな演算子のテンソル積を用いて合成演算子の構築や、演算子の合成といったいくつかのメソッドがあります。

Operator の作成

operator オブジェクト作成のもっとも簡単な方法は、Numpy 配列のリストとして行列を指定し、それで初期化することです。例えば、2量子ビット Pauli-XX 演算子を作成するには:

[8]:
XX = Operator([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])
XX
[8]:
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))

Operator プロパティ

Operator オブジェクトは配下の行列とサブシステムのインプット、アウトプット次元を保持します。

  • data: 配下の Numpy 配列にアクセス。Operator.data プロパティーも使用する。

  • dims: operator の総インプット、アウトプット次元を返却。Operator.dim プロパティーも利用。注:アウトプットは、タプル ``(input_dim, output_dim)`` 形式で、配下の行列のシェイプの逆です

[9]:
XX.data
[9]:
array([[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]])
[10]:
input_dim, output_dim = XX.dim
input_dim, output_dim
[10]:
(4, 4)

インプットアウトプット次元

Operator クラスはまたサブシステムの事前も追跡し、operator を合成するときに使われます。これらは、input_dimsoutput_dims 関数で取得できます。

\(2^N\) 掛ける \(2^M\) operatorの場合、インプットとアウトプットの次元は、 M-qubit と N-qubit と自動的に想定されます。

[11]:
op = Operator(np.random.rand(2 ** 1, 2 ** 2))
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())
Input dimensions: (2, 2)
Output dimensions: (2,)

入力の行列が量子ビットサブシステムに分割できない場合には、単一量子ビット operator として保持されます。例えば、 \(6\times6\) 行列の場合:

[12]:
op = Operator(np.random.rand(6, 6))
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())
Input dimensions: (6,)
Output dimensions: (6,)

インプットとアウトプット次元は新しい operator の初期化時に手動で指定することも出来ます。

[13]:
# Force input dimension to be (4,) rather than (2, 2)
op = Operator(np.random.rand(2 ** 1, 2 ** 2), input_dims=[4])
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())
Input dimensions: (4,)
Output dimensions: (2,)
[14]:
# Specify system is a qubit and qutrit
op = Operator(np.random.rand(6, 6),
              input_dims=[2, 3], output_dims=[2, 3])
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())
Input dimensions: (2, 3)
Output dimensions: (2, 3)

input_dimsoutput_dims 関数を利用して、サブシステムのサブセットのインプットとアウトプット次元のみを求めることも出来ます。

[15]:
print('Dimension of input system 0:', op.input_dims([0]))
print('Dimension of input system 1:', op.input_dims([1]))
Dimension of input system 0: (2,)
Dimension of input system 1: (3,)

クラスをオペレーターに変換する

Qiskit の幾つかの他のクラスは、operator の初期化方法を利用して、Operator オブジェクトに変換することができます。例えば:

  • Pauli オブジェクト

  • GateInstruction オブジェクト

  • QuantumCircuits オブジェクト

最後の点は、``Operator``クラスにより、量子回路の最終ユニタリ行列の計算のためのユニタリシミュレーターとして使うことができ、シミュレーターのバックエンドを呼び出す必要がないことに着目してください。回路にサポートされない演算子が含まれている場合には、例外が発生します。サポートされていない演算子は: measure、reset、条件付き演算子、行列定義のないゲート、もしくは 行列定義があるゲートだがその分解(decomposition) です。

[16]:
# Create an Operator from a Pauli object

pauliXX = Pauli(label='XX')
Operator(pauliXX)
[16]:
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))
[19]:
# Create an Operator for a Gate object
Operator(CXGate())
[19]:
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))
[20]:
# Create an operator from a parameterized Gate object
Operator(RXGate(np.pi / 2))
[20]:
Operator([[0.70710678+0.j        , 0.        -0.70710678j],
          [0.        -0.70710678j, 0.70710678+0.j        ]],
         input_dims=(2,), output_dims=(2,))
[21]:
# 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)
[21]:
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))

回路にオペレーターを利用する

ユニタリな OperatorQuantumCircuit.append メソッドを利用して QuantumCircuit に直接追加出来ます。これにより、Operator`UnitaryGate オブジェクトに変換され、回路に追加されます。

operator がユニタリでない場合には、例外が発生します。Operator.is_unitary() 関数でチェックができ、operator がユニタリの場合は True、それ以外の場合は False になります。

[22]:
# Create an operator
XX = Operator(Pauli(label='XX'))

# Add to a circuit
circ = QuantumCircuit(2, 2)
circ.append(XX, [0, 1])
circ.measure([0,1], [0,1])
circ.draw('mpl')
[22]:
../../_images/tutorials_circuits_advanced_2_operators_overview_22_0.png

上記の例では、Pauli オブジェクトから operator を初期化していることに注目してください。しかしながら、Pauli オブジェクトは回路にそれ自体で直接追加可能であり、この場合単一量子ビットの パウリゲート列として変換されます。

[23]:
backend = BasicAer.get_backend('qasm_simulator')
job = execute(circ, backend, basis_gates=['u1','u2','u3','cx'])
job.result().get_counts(0)
[23]:
{'11': 1024}
[24]:
# Add to a circuit
circ2 = QuantumCircuit(2, 2)
circ2.append(Pauli(label='XX'), [0, 1])
circ2.measure([0,1], [0,1])
circ2.draw()
[24]:
     ┌───────────┐┌─┐
q_0: ┤0          ├┤M├───
     │  Pauli:XX │└╥┘┌─┐
q_1: ┤1          ├─╫─┤M├
     └───────────┘ ║ └╥┘
c: 2/══════════════╩══╩═
                   0  1 

オペレーターを結合する

オペレーターはいくつかの方法で結合出来ます。

テンソル積

2つの operator \(A\) and \(B\) は、Operator.tensor 関数を利用して、テンソル積 operator \(A\otimes B\) に結合出来ます。A と B の両者が単一量子ビット operator の場合、A.tensor(B) = \(A\otimes B\) は 行列 B がサブシステム 0 で、行列A がサブシステム 1となるようにインデックス化されたサブシステムになることに着目してください。

[25]:
A = Operator(Pauli(label='X'))
B = Operator(Pauli(label='Z'))
A.tensor(B)
[25]:
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つの operator \(A\)\(B\) により、 A.expand(B) = \(B\otimes A\) とすると、行列 Aがサブシステム 0 で、行列B がサブシステム 1となるようにインデックス化されたサブシステムになります。

[26]:
A = Operator(Pauli(label='X'))
B = Operator(Pauli(label='Z'))
A.expand(B)
[26]:
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))

コンポジション

2つの operator \(A\) の掛け算を Operator.compose` メソッドを利用して合成出来ます。A.compose(B) の結果として、行列 \(B.A\) となる operator が返却されます:

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

composefront kwarg を設定すると、\(A\) の前に、\(B\) を適用するように逆順の合成ができます:A.compose(B, front=True) = \(A.B\)

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

サブシステム合成

前の合成は最初の operator \(A\) の総アウトプット次元が、合成される operator \(B\) の総インプット次元と同じであるが必要なことに注意してください(同様に、\(B\) のインプット次元は、front=True で合成される場合、等しくなる必要があります)。

大きな operator から composeqargs kwarg を指定して front=True 設定があるかないかに関わらず、サブシステムを選択し小さな operator を合成できます。この場合、合成される関係するサブシステムのインプット、アウトプット次元は一致する必要があります。注: 小さな operator は ``compose`` メソッドの引数である必要があります。

例えば、3量子ビット Operator から2量子ビットのゲートを合成するには:

[29]:
# Compose XZ with an 3-qubit identity operator
op = Operator(np.eye(2 ** 3))
XZ = Operator(Pauli(label='XZ'))
op.compose(XZ, qargs=[0, 2])
[29]:
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))
[30]:
# Compose YX in front of the previous operator
op = Operator(np.eye(2 ** 3))
YX = Operator(Pauli(label='YX'))
op.compose(XZ, qargs=[0, 2], front=True)
[30]:
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))

線型結合

また、オペレーターは足し算、引き算、あるいは 複素数でのスカラー掛け算のための標準な線型オペレーターを利用した結合が出来ます。

[31]:
XX = Operator(Pauli(label='XX'))
YY = Operator(Pauli(label='YY'))
ZZ = Operator(Pauli(label='ZZ'))

op = 0.5 * (XX + YY - 3 * ZZ)
op
[31]:
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))

重要な点として、tensorexpand 及び compose はユニタリ operator のユニタリ性を保ちますが、一方 線型結合はそうでないこと、従って2つのユニタリ operator の足し算は、一般的に非ユニタリ operator になります:

[32]:
op.is_unitary()
[32]:
False

オペレーターへの暗黙的変換

以下の全てのメソッドでは2番目のオブジェクトがまだ Operator オブジェクトでない場合にはメソッドにより暗黙的に変換されることに注意してください。これが意味するところは、最初に Operator オブジェクトに明示的に変換しなくても、直接行列を渡せるということです。もし変換が出来ない場合には例外が発生します。

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

オペレーターの比較

オペレーター には二つの operator が近似的に一致しているかをチェックする equality メソッドがあります。

[34]:
Operator(Pauli(label='X')) == Operator(XGate())
[34]:
True

ここではオペレーターの行列要素が近似的に一致しているかをチェックしますが、グローバル位相が異なる2つのユニタリは同じとは見なされないことに注意してください。

[35]:
Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
[35]:
False

プロセスフィデリティー

Quantum Information モジュールの process_fidelity 関数を使って operator を比較することも出来ます。これは2つの量子チャネルが情報理論的な量としてどれだけ近いかを示し、ユニタリオペレーターの場合にはグローバル位相に依存しません。

[36]:
# 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 でない場合には例外が発生します。

[37]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
QiskitNone
Terra0.14.0
Aer0.6.0
Ignis0.3.0
Aqua0.7.0
IBM Q Provider0.6.1
System information
Python3.7.7 (default, Mar 26 2020, 10:32:53) [Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs4
Memory (Gb)16.0
Wed Apr 29 12:36:22 2020 EDT

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.

[ ]: