Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Клас Operator

Package versions

Код на цій сторінці розроблено з використанням наведених нижче залежностей. Рекомендуємо використовувати ці або новіші версії.

qiskit[all]~=2.5.2

Ця сторінка показує, як використовувати клас Operator. Загальний огляд представлень операторів у Qiskit, включно з класом Operator та іншими, дивись у розділі Огляд класів операторів.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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 у схемах​

Унітарні Operators можна безпосередньо вставляти в 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")

Output of the previous code cell

Зверни увагу, що у наведеному прикладі оператор ініціалізується з об'єкта 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

Комбінування операторів​

Оператори можна комбінувати кількома способами.

Тензорний добуток​

Два оператори AA та BB можна об'єднати в оператор тензорного добутку A⊗BA\otimes B за допомогою функції Operator.tensor. Зверни увагу: якщо обидва оператори AA і BB є однокубітними, то A.tensor(B) = A⊗BA\otimes B матиме підсистеми, де матриця BB відповідає підсистемі 0, а матриця AA — підсистемі 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, яка діє як тензорний добуток, але у зворотному порядку. Отже, для двох операторів AA та BB маємо A.expand(B) = B⊗AB\otimes A, де матриця AA відповідає підсистемі 0, а матриця BB — підсистемі 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))

Композиція​

Також можна скомпонувати два оператори AA та BB для матричного множення за допомогою методу Operator.compose. A.compose(B) повертає оператор з матрицею B.AB.A:

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,))

Також можна виконати композицію у зворотному порядку, застосувавши BB перед AA за допомогою аргументу front методу compose: A.compose(B, front=True) = A.BA.B:

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,))

Композиція підсистем​

Зверни увагу, що попередня композиція вимагає, щоб загальна вихідна розмірність першого оператора AA дорівнювала загальній вхідній розмірності оператора BB, що компонується (і аналогічно — вихідна розмірність BB має дорівнювати вхідній розмірності AA при компонуванні з front=True).

Також можна скомпонувати менший оператор із вибраними підсистемами більшого оператора за допомогою аргументу qargs методу compose, як із front=True, так і без нього. У цьому випадку відповідні вхідні та вихідні розмірності підсистем, що компонуються, мають збігатися. Зверни увагу, що менший оператор завжди має бути аргументом методу compose.

Наприклад, щоб скомпонувати двокубітний гейт із трикубітним оператором:

# 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.

Наступні кроки​

Рекомендації