Zum Hauptinhalt springen

Die Operator-Klasse

Paketversionen

Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.

qiskit[all]~=2.3.0

Diese Seite zeigt, wie du die Operator-Klasse verwendest. Einen Überblick über Operator-Darstellungen in Qiskit, einschließlich der Operator-Klasse und weiterer, findest du unter Übersicht der Operator-Klassen.

# 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

Klassen in Operators umwandeln

Mehrere andere Klassen in Qiskit können mithilfe der Operator-Initialisierungsmethode direkt in ein Operator-Objekt umgewandelt werden. Zum Beispiel:

  • Pauli-Objekte
  • Gate- und Instruction-Objekte
  • QuantumCircuit-Objekte

Beachte, dass der letzte Punkt bedeutet, dass du die Operator-Klasse als unitären Simulator verwenden kannst, um die abschließende unitäre Matrix für einen Quantum Circuit zu berechnen, ohne einen Simulator-Backend aufrufen zu müssen. Falls der Circuit nicht unterstützte Operationen enthält, wird eine Ausnahme ausgelöst. Nicht unterstützte Operationen sind: Messen, Zurücksetzen, bedingte Operationen oder ein Gate, das keine Matrixdefinition oder Zerlegung in Gates mit Matrixdefinitionen besitzt.

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

Operators in Circuits verwenden

Unitäre Operators können mithilfe der QuantumCircuit.append-Methode direkt in einen QuantumCircuit eingefügt werden. Dabei wird der Operator in ein UnitaryGate-Objekt umgewandelt, das dem Circuit hinzugefügt wird.

Falls der Operator nicht unitär ist, wird eine Ausnahme ausgelöst. Dies kann mit der Funktion Operator.is_unitary() überprüft werden, die True zurückgibt, wenn der Operator unitär ist, und andernfalls 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

Beachte, dass im obigen Beispiel der Operator aus einem Pauli-Objekt initialisiert wird. Das Pauli-Objekt kann jedoch auch direkt in den Circuit eingefügt werden und wird dann in eine Folge von Einzel-Qubit-Pauli-Gates umgewandelt:

# 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

Operators kombinieren

Operators können auf verschiedene Weisen miteinander kombiniert werden.

Tensorprodukt

Zwei Operatoren AA und BB können mithilfe der Funktion Operator.tensor zu einem Tensorprodukt-Operator ABA\otimes B kombiniert werden. Wenn sowohl AA als auch BB Einzel-Qubit-Operatoren sind, gilt: A.tensor(B) = ABA\otimes B hat die Teilsysteme so indiziert, dass Matrix BB auf Teilsystem 0 und Matrix AA auf Teilsystem 1 liegt.

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

Tensor-Expansion

Eine eng verwandte Operation ist Operator.expand, die wie ein Tensorprodukt funktioniert, jedoch in umgekehrter Reihenfolge. Für zwei Operatoren AA und BB gilt daher: A.expand(B) = BAB\otimes A, wobei die Teilsysteme so indiziert sind, dass Matrix AA auf Teilsystem 0 und Matrix BB auf Teilsystem 1 liegt.

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

Komposition

Du kannst zwei Operatoren AA und BB auch mittels der Methode Operator.compose zur Matrixmultiplikation verknüpfen. A.compose(B) gibt den Operator mit Matrix B.AB.A zurück:

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

Du kannst die Komposition auch in umgekehrter Reihenfolge durchführen, indem du BB vor AA anwendest, mithilfe des front-Kwargs von 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,))

Teilsystem-Komposition

Beachte, dass bei der vorherigen Komposition die gesamte Ausgabedimension des ersten Operators AA gleich der gesamten Eingabedimension des komponierten Operators BB sein muss (und entsprechend muss die Ausgabedimension von BB gleich der Eingabedimension von AA sein, wenn mit front=True komponiert wird).

Du kannst auch einen kleineren Operator mit einer Auswahl von Teilsystemen eines größeren Operators verknüpfen, indem du das qargs-Kwarg von compose verwendest – mit oder ohne front=True. In diesem Fall müssen die relevanten Eingabe- und Ausgabedimensionen der komponierten Teilsysteme übereinstimmen. Beachte, dass der kleinere Operator immer das Argument der compose-Methode sein muss.

Um beispielsweise ein Zwei-Qubit-Gate mit einem Drei-Qubit-Operator zu verknüpfen:

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

Linearkombinationen

Operators können auch mithilfe von Standard-Linearoperatoren für Addition, Subtraktion und skalare Multiplikation mit komplexen Zahlen kombiniert werden.

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

Ein wichtiger Punkt ist, dass tensor, expand und compose die Unitarität unitärer Operatoren erhalten, Linearkombinationen jedoch nicht. Das Addieren zweier unitärer Operatoren ergibt daher im Allgemeinen einen nicht-unitären Operator:

op.is_unitary()
False

Implizite Umwandlung zu Operators

Beachte, dass bei allen folgenden Methoden ein zweites Objekt, das noch kein Operator-Objekt ist, von der Methode implizit in eines umgewandelt wird. Das bedeutet, dass Matrizen direkt übergeben werden können, ohne sie zuvor explizit in einen Operator umzuwandeln. Falls die Umwandlung nicht möglich ist, wird eine Ausnahme ausgelöst.

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

Operators vergleichen

Operators implementieren eine Gleichheitsmethode, mit der geprüft werden kann, ob zwei Operatoren näherungsweise gleich sind.

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

Beachte, dass dabei jedes Matrixelement der Operatoren näherungsweise verglichen wird; zwei unitäre Operatoren, die sich nur durch eine globale Phase unterscheiden, gelten nicht als gleich:

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

Prozesstreue

Du kannst Operatoren auch mithilfe der Funktion process_fidelity aus dem Modul Quantum Information vergleichen. Dies ist eine informationstheoretische Größe, die angibt, wie nah sich zwei Quantenkanäle sind. Bei unitären Operatoren hängt sie nicht von der globalen Phase ab.

# 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

Beachte, dass die Prozesstreue im Allgemeinen nur dann ein gültiges Näherungsmaß ist, wenn die Eingabeoperatoren unitär sind (oder CP im Fall von Quantenkanälen), und eine Ausnahme ausgelöst wird, wenn die Eingaben nicht CP sind.

Nächste Schritte

Empfehlungen