Operator 클래스
패키지 버전
이 페이지의 코드는 다음 요구 사항을 사용하여 개발되었습니다. 이 버전 이상을 사용하는 것을 권장합니다.
qiskit[all]~=2.3.0
이 페이지에서는 Operator 클래스를 사용하는 방법을 설명합니다. Operator 클래스 및 기타 클래스를 포함한 Qiskit의 연산자 표현에 대한 개략적인 개요는 연산자 클래스 개요를 참조 하세요.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import CXGate, RXGate, XGate
from qiskit.quantum_info import Operator, Pauli, process_fidelity
클래스를 Operator로 변환하기
Qiskit의 여러 다른 클래스는 연산자 초기화 메서드를 사용하여 Operator 객체로 직접 변환할 수 있습니다. 예를 들면 다음과 같습니다:
Pauli객체Gate및Instruction객체QuantumCircuit객체
마지막 항목의 의미는, 시뮬레이터 Backend를 호출하지 않고도 Operator 클래스를 유니터리 시뮬레이터로 사용하여 양자 Circuit의 최종 유니터리 행렬을 계산할 수 있다는 것입니다. Circuit에 지원되지 않는 연산이 포함되어 있으면 예외가 발생합니다. 지원되지 않는 연산은 측정(measure), 리셋(reset), 조건부 연산, 또는 행렬 정의나 행렬 정의가 있는 Gate로의 분해가 없는 Gate입니다.
# Create an Operator from a Pauli object
pauliXX = Pauli("XX")
Operator(pauliXX)
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))
# Create an Operator for a Gate object
Operator(CXGate())
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))
# Create an operator from a parameterized Gate object
Operator(RXGate(np.pi / 2))
Operator([[0.70710678+0.j , 0. -0.70710678j],
[0. -0.70710678j, 0.70710678+0.j ]],
input_dims=(2,), output_dims=(2,))
# 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)
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))
Circuit에서 Operator 사용하기
유니터리 Operators는 QuantumCircuit.append 메서드를 사용하여 QuantumCircuit에 직접 삽입할 수 있습니다. 이렇게 하면 Operator가 UnitaryGate 객체로 변환 되어 Circuit에 추가됩니다.
연산자가 유니터리가 아닌 경우 예외가 발생합니다. 이는 Operator.is_unitary() 함수를 사용하여 확인할 수 있으며, 연산자가 유니터리이면 True를, 그렇지 않으면 False를 반환합니다.
# Create an operator
XX = Operator(Pauli("XX"))
# Add to a circuit
circ = QuantumCircuit(2, 2)
circ.append(XX, [0, 1])
circ.measure([0, 1], [0, 1])
circ.draw("mpl")
위 예제에서 연산자는 Pauli 객체로부터 초기화됩니다. 그러나 Pauli 객체를 Circuit에 직접 삽입할 수도 있으며, 이 경우 단일 Qubit Pauli Gate의 시퀀스로 변환됩니다:
# Add to a circuit
circ2 = QuantumCircuit(2, 2)
circ2.append(Pauli("XX"), [0, 1])
circ2.measure([0, 1], [0, 1])
circ2.draw()
┌────────────┐┌─┐
q_0: ┤0 ├┤M├───
│ Pauli(XX) │└╥┘┌─┐
q_1: ┤1 ├─╫─┤M├
└────────────┘ ║ └╥┘
c: 2/═══════════════╩══╩═
0 1
Operator 결합하기
Operator는 여러 방법으로 결합할 수 있습니다.
텐서곱
두 연산자 와 는 Operator.tensor 함수를 사용하여 텐서곱 연산자 로 결합할 수 있습니다. 와 가 모두 단일 Qubit 연산자인 경우, A.tensor(B) = 는 서브시스템 0에 행렬 , 서브시스템 1에 행렬 로 인덱싱됩니다.
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.tensor(B)
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가 있으며, 이는 텐서곱과 유사하지만 순서가 반대입니다. 따라서 두 연산자 와 에 대해 A.expand(B) = 이며, 서브시스템 0에 행렬 , 서브시스템 1에 행렬 로 인덱싱됩니다.
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.expand(B)
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))
합성
Operator.compose 메서드를 사용하여 두 연산자 와 를 합성하여 행렬 곱셈을 구현할 수 있습니다. A.compose(B)는 행렬 를 갖는 연산자를 반환합니다:
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.compose(B)
Operator([[ 0.+0.j, 1.+0.j],
[-1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))
compose의 front 키워드 인수를 사용하여 앞에 를 적용하는 역순으로 합성할 수도 있습니다: A.compose(B, front=True) = :
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.compose(B, front=True)
Operator([[ 0.+0.j, -1.+0.j],
[ 1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))
서브시스템 합성
이전의 합성 방법은 첫 번째 연산자 의 전체 출력 차원이 합성할 연산자 의 전체 입력 차원과 같아야 합니다 (마찬가지로, front=True로 합성할 때 의 출력 차원은 의 입력 차원과 같아야 합니다).
compose의 qargs 키워드 인수를 사용하여, front=True 유무에 상관없이 더 큰 연산자의 서브시스템 선택과 더 작은 연산자를 합성할 수 있습니다. 이 경우, 합성되는 서브시스템의 관련 입력 및 출력 차원이 일치해야 합니다. 더 작은 연산자는 항상 compose 메서드의 인수여야 합니다.
예를 들어, 2-Qubit Gate를 3-Qubit 연산자와 합성하려면:
# Compose XZ with a 3-qubit identity operator
op = Operator(np.eye(2**3))
XZ = Operator(Pauli("XZ"))
op.compose(XZ, qargs=[0, 2])
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))
# Compose YX in front of the previous operator
op = Operator(np.eye(2**3))
YX = Operator(Pauli("YX"))
op.compose(YX, qargs=[0, 2], front=True)
Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j],
[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.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.-1.j],
[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j],
[0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
[0.+1.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.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
[0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],
input_dims=(2, 2, 2), output_dims=(2, 2, 2))
선형 결합
Operator는 덧셈, 뺄셈, 복소수에 의한 스칼라 곱 등 표준 선형 연산자를 사용하여 결합할 수도 있습니다.
XX = Operator(Pauli("XX"))
YY = Operator(Pauli("YY"))
ZZ = Operator(Pauli("ZZ"))
op = 0.5 * (XX + YY - 3 * ZZ)
op
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))
중요한 점은, tensor, expand, compose는 유니터리 연산자의 유니터리성을 보존하지만, 선형 결합은 그렇지 않다는 것입니다. 따라서 두 유니터리 연산자를 더하면 일반적으로 비유니터리 연산자가 됩니다:
op.is_unitary()
False
Operator로의 암묵적 변환
다음의 모든 메서드에서, 두 번째 객체가 이미 Operator 객체가 아닌 경우, 메서드에 의해 암묵적으로 Operator로 변환됩니다. 즉, 행렬을 명시적으로 Operator로 변환하지 않고 직접 전달할 수 있습니다. 변환이 불가능한 경우 예외가 발생합니다.
# Compose with a matrix passed as a list
Operator(np.eye(2)).compose([[0, 1], [1, 0]])
Operator([[0.+0.j, 1.+0.j],
[1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))
Operator 비교하기
Operator는 두 연산자가 근사적으로 같은지 확인하는 데 사용할 수 있는 등호 메서드를 구현합니다.
Operator(Pauli("X")) == Operator(XGate())
True
이것은 연산자의 각 행렬 원소가 근사적으로 같은지를 확인합니다. 전역 위상만 다른 두 유니터리는 같다고 간주되지 않습니다:
Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
False
프로세스 충실도
양자 정보 모듈의 process_fidelity 함수를 사용하여 연산자를 비교할 수도 있습니다. 이는 두 양자 채널이 얼마나 유사한지를 나타내는 정보 이론적 양으로, 유니터리 연산자의 경우 전역 위상에 의존하지 않습니다.
# 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가 아닌 경우 예외가 발생합니다.
다음 단계
- Operator API 참조 문서를 살펴보세요.