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-ObjekteGate- undInstruction-ObjekteQuantumCircuit-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")
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 und können mithilfe der Funktion Operator.tensor zu einem Tensorprodukt-Operator kombiniert werden. Wenn sowohl als auch Einzel-Qubit-Operatoren sind, gilt: A.tensor(B) = hat die Teilsysteme so indiziert, dass Matrix auf Teilsystem 0 und Matrix 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 und gilt daher: A.expand(B) = , wobei die Teilsysteme so indiziert sind, dass Matrix auf Teilsystem 0 und Matrix 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 und auch mittels der Methode Operator.compose zur Matrixmultiplikation verknüpfen. A.compose(B) gibt den Operator mit Matrix 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 vor anwendest, mithilfe des front-Kwargs von 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,))
Teilsystem-Komposition
Beachte, dass bei der vorherigen Komposition die gesamte Ausgabedimension des ersten Operators gleich der gesamten Eingabedimension des komponierten Operators sein muss (und entsprechend muss die Ausgabedimension von gleich der Eingabedimension von 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
- Erkunde die Operator-API-Referenz.