Клас Operator
Версії пакетів
Код на цій сторінці розроблено з використанням наведених нижче залежностей. Рекомендуємо використовувати ці або новіші версії.
qiskit[all]~=2.3.0
Ця сторінка показує, як використовувати клас Operator. Загальний огляд представлень операторів у Qiskit, включно з класом Operator та іншими, дивись у розділі Огляд класів операторів.
# 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
Зверни увагу: останній пункт означає, що клас Operator можна використовувати як симулятор унітарних перетворень для обчислення фінальної унітарної матриці квантової схеми — без виклику бекенду симулятора. Якщо схема містить непідтримувані операції, виникає виняток. Непідтримувані операції: вимірювання, скидання (reset), умовні операції, а також гейт, що не має матричного визначення або декомпозиції через гейти з матричними визначеннями.
# 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))
Використання Operator у схемах
Унітарні Operator-и можна безпосередньо вставляти в QuantumCircuit за допомогою методу QuantumCircuit.append. Це перетворює Operator на об'єкт UnitaryGate, який додається до схеми.
Якщо оператор не є унітарним, виникає виняток. Це можна перевірити за допомогою функції 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 також можна вставити безпосередньо в схему, і тоді він буде перетворений на послідовність однокубітних гейтів Паулі:
# 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.tensor. Зверни увагу: якщо обидва оператори і є однокубітними, то 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,))
Також можна виконати композицію у зворотному порядку, застосувавши перед за допомогою аргументу front методу compose: 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).
Також можна скомпонувати менший оператор із вибраними підсистемами більшого оператора за допомогою аргументу qargs методу compose, як із front=True, так і без нього. У цьому випадку відповідні вхідні та вихідні розмірності підсистем, що компонуються, мають збігатися. Зверни увагу, що менший оператор завжди має бути аргументом методу compose.
Наприклад, щоб скомпонувати двокубітний гейт із тrikубітним оператором:
# 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))
Лінійні комбінації
Оператори також можна комбінувати за допомогою стандартних лінійних операцій: додавання, віднімання та множення на комплексні числа.
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. Якщо перетворення неможливе, виникає виняток.
# 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(Pauli("X")) == Operator(XGate())
True
Зверни увагу: перевіряється приблизна рівність кожного елемента матриці операторів; два унітарні оператори, що відрізняються глобальною фазою, не вважаються рівними:
Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
False
Процесна вірність
Також можна порівнювати оператори за допомогою функції process_fidelity з модуля Quantum Information. Це інформаційно-теоретична величина, що показує, наскільки два квантові канали близькі один до одного; у випадку унітарних операторів вона не залежить від гло бальної фази.
# 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.