Przejdź do głównej treści

Klasa Operator

Wersje pakietów

Kod na tej stronie został opracowany przy użyciu poniższych wymagań. Zalecamy korzystanie z tych lub nowszych wersji.

qiskit[all]~=2.3.0

Ta strona pokazuje, jak używać klasy Operator. Ogólne omówienie reprezentacji operatorów w Qiskit, w tym klasy Operator i innych, znajdziesz w artykule Przegląd klas operatorów.

# 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

Konwertowanie klas na obiekty Operator

Kilka innych klas w Qiskit można bezpośrednio przekonwertować na obiekt Operator za pomocą metody inicjalizacji operatora. Na przykład:

  • Obiekty Pauli
  • Obiekty Gate i Instruction
  • Obiekty QuantumCircuit

Warto zauważyć, że ostatni punkt oznacza, że możesz używać klasy Operator jako symulatora unitarnego do obliczania końcowej macierzy unitarnej dla Circuit, bez konieczności wywoływania Backend symulatora. Jeśli Circuit zawiera nieobsługiwane operacje, zostanie zgłoszony wyjątek. Nieobsługiwane operacje to: pomiar, reset, operacje warunkowe lub Gate, która nie posiada definicji macierzowej ani rozkładu na Gate z definicjami macierzowymi.

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

Używanie obiektów Operator w Circuit

Unitarne obiekty Operator można bezpośrednio wstawiać do QuantumCircuit za pomocą metody QuantumCircuit.append. Konwertuje to Operator na obiekt UnitaryGate, który jest dodawany do Circuit.

Jeśli operator nie jest unitarny, zostanie zgłoszony wyjątek. Można to sprawdzić funkcją Operator.is_unitary(), która zwraca True, gdy operator jest unitarny, i False w przeciwnym razie.

# 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

Zauważ, że w powyższym przykładzie operator jest inicjalizowany z obiektu Pauli. Jednak obiekt Pauli można też wstawić bezpośrednio do Circuit, gdzie zostanie przekonwertowany na sekwencję jednoQubitowych bramek 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

Łączenie obiektów Operator

Operatory można łączyć na kilka sposobów.

Iloczyn tensorowy

Dwa operatory AA i BB można połączyć w operator iloczynu tensorowego ABA\otimes B przy użyciu funkcji Operator.tensor. Jeśli oba AA i BB są operatorami jednoQubitowymi, to A.tensor(B) = ABA\otimes B będzie miało podsystemy zaindeksowane tak, że macierz BB odpowiada podsystemowi 0, a macierz AA podsystemowi 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))

Rozwinięcie tensorowe

Ściśle powiązaną operacją jest Operator.expand, która działa jak iloczyn tensorowy, ale w odwrotnej kolejności. Dla dwóch operatorów AA i BB mamy A.expand(B) = BAB\otimes A, gdzie podsystemy są zaindeksowane tak, że macierz AA odpowiada podsystemowi 0, a macierz BB podsystemowi 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))

Składanie

Możesz również złożyć dwa operatory AA i BB, aby zrealizować mnożenie macierzy, korzystając z metody Operator.compose. A.compose(B) zwraca operator z macierzą 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,))

Możesz też złożyć w odwrotnej kolejności, stosując BB przed AA, używając argumentu front metody 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,))

Składanie podsystemów

Zauważ, że poprzednie składanie wymaga, aby całkowity wymiar wyjściowy pierwszego operatora AA był równy całkowitemu wymiarowi wejściowemu składanego operatora BB (analogicznie, wymiar wyjściowy BB musi być równy wymiarowi wejściowemu AA przy składaniu z front=True).

Możesz też złożyć mniejszy operator z wybranym podzbiorem podsystemów większego operatora, używając argumentu qargs metody compose, zarówno z front=True, jak i bez niego. W takim przypadku odpowiednie wymiary wejściowe i wyjściowe składanych podsystemów muszą być zgodne. Pamiętaj, że mniejszy operator musi zawsze być argumentem metody compose.

Na przykład, aby złożyć bramkę dwu-qubitową z operatorem trzy-qubitowym:

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

Kombinacje liniowe

Operatory można też łączyć za pomocą standardowych operacji liniowych: dodawania, odejmowania i mnożenia skalarnego przez liczby zespolone.

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

Ważna uwaga: o ile tensor, expand i compose zachowują unitarność operatorów unitarnych, kombinacje liniowe jej nie zachowują — w ogólności suma dwóch operatorów unitarnych daje operator nieunitarny:

op.is_unitary()
False

Niejawna konwersja do operatorów

Zwróć uwagę, że we wszystkich poniższych metodach, jeśli drugi obiekt nie jest jeszcze obiektem Operator, jest on niejawnie konwertowany przez metodę. Oznacza to, że macierze można przekazywać bezpośrednio, bez wcześniejszej jawnej konwersji do Operator. Jeśli konwersja nie jest możliwa, zgłaszany jest wyjątek.

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

Porównywanie operatorów

Operatory implementują metodę równości, której można użyć do sprawdzenia, czy dwa operatory są w przybliżeniu równe.

Operator(Pauli("X")) == Operator(XGate())
True

Pamiętaj, że porównanie sprawdza, czy każdy element macierzy operatorów jest w przybliżeniu równy; dwa operatory unitarne różniące się globalną fazą nie są uznawane za równe:

Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
False

Wierność procesu

Możesz też porównywać operatory za pomocą funkcji process_fidelity z modułu Quantum Information. Jest to wielkość teorioiformacyjna określająca, jak bliskie sobie są dwa kanały kwantowe; w przypadku operatorów unitarnych nie zależy od globalnej fazy.

# 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

Pamiętaj, że wierność procesu jest ogólnie poprawną miarą bliskości tylko wtedy, gdy operatory wejściowe są unitarne (lub CP w przypadku kanałów kwantowych); wyjątek jest zgłaszany, jeśli dane wejściowe nie są CP.

Następne kroki

Rekomendacje