Třída Operator
Verze balíčků
Kód na této stránce byl vyvinut s použitím následujících požadavků. Doporučujeme používat tyto nebo novější verze.
qiskit[all]~=2.3.0
Tato stránka ukazuje, jak používat třídu Operator. Pro přehled reprezentací operátorů v Qiskitu na vysoké úrovni, včetně třídy Operator a dalších, viz Přehled tříd operátorů.
# 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
Převod tř íd na Operators
Několik dalších tříd v Qiskitu lze přímo převést na objekt Operator pomocí metody inicializace operátoru. Například:
- objekty
Pauli - objekty
GateaInstruction - objekty
QuantumCircuit
Všimni si, že poslední bod znamená, že třídu Operator lze použít jako unitární simulátor pro výpočet výsledné unitární matice kvantového Circuit, aniž bys musel/a volat Backend simulátoru. Pokud Circuit obsahuje nepodporované operace, je vyvolána výjimka. Nepodporované operace jsou: measure, reset, podmíněné operace nebo Gate, která nemá definici matice ani rozklad pomocí Gates s definicemi matic.
# 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))
Použití Operators v Circuits
Unitární Operators lze přímo vkládat do QuantumCircuit pomocí metody QuantumCircuit.append. Tím se Operator převede na objekt UnitaryGate, který je přidán do Circuit.
Pokud operátor není unitární, je vyvolána výjimka. To lze zkontrolovat pomocí funkce Operator.is_unitary(), která vrátí True, pokud je operátor unitární, a False v opačném případě.
# 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")
Všimni si, že v předchozím příkladu je operátor inicializován z objektu Pauli. Nicméně objekt Pauli lze také přímo vložit do Circuit samotného a bude převeden na posloupnost jednoqubitových Pauli gates:
# 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
Kombinování Operators
Operators lze kombinovat několika způsoby.
Tenzorový součin
Dva operátory a lze zkombinovat do tenzorového součinu pomocí funkce Operator.tensor. Všimni si, že pokud jsou i jednoqubitové operátory, pak A.tensor(B) = bude mít subsystémy indexované jako matice na subsystému 0 a matice na subsystému 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))
Tenzorové rozvinutí
Úzce příbuznou operací je Operator.expand, která funguje jako tenzorový součin, ale v opačném pořadí. Pro dva operátory a platí A.expand(B) = , kde subsystémy jsou indexovány jako matice na subsystému 0 a matice na subsystému 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))
Kompozice
Dva operátory a lze také skládat pro provádění maticového násobení pomocí metody Operator.compose. A.compose(B) vrátí operátor s maticí :
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,))
Lze také skládat v opačném pořadí aplikací před pomocí argumentu front metody 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,))
Kompozice subsystémů
Všimni si, že předchozí kompozice vyžaduje, aby celková výstupní dimenze prvního operátoru byla rovna celkové vstupní dimenzi skládaného operátoru (a podobně výstupní dimenze musí být rovna vstupní dimenzi při skládání s front=True).
Lze také skládat menší operátor s výběrem subsystémů na větším operátoru pomocí argumentu qargs metody compose, a to s front=True i bez něj. V tomto případě musí relevantní vstupní a výstupní dimenze skládaných subsystémů souhlasit. Všimni si, že menší operátor musí být vždy argumentem metody compose.
Například pro složení dvouqubitového Gate s tříqubitovým operátorem:
# 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))
Lineární kombinace
Operators lze také kombinovat pomocí standardních lineárních operátorů pro sčítání, odčítání a skalární násobení komplexními čísly.
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))
Důležitou poznámkou je, že zatímco tensor, expand a compose zachovávají unitaritu unitárních operátorů, lineární kombinace to nezaručují; sčítání dvou unitárních operátorů obecně vede k neunitárnímu operátoru:
op.is_unitary()
False
Implicitní převod na Operators
Všimni si, že pro všechny následující metody platí, že pokud druhý objekt již není objektem Operator, je metodou implicitně převeden. To znamená, že matice lze předávat přímo, aniž by bylo nutné je explicitně převádět na Operator. Pokud převod není možný, je vyvolána výjimka.
# 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,))
Porovnání Operators
Operators implementují metodu rovnosti, kterou lze použít ke kontrole, zda jsou dva operátory přibližně stejné.
Operator(Pauli("X")) == Operator(XGate())
True
Všimni si, že tato kontrola ověřuje, zda je každý prvek matice operátorů přibližně stejný; dva unitární operátory, které se liší o globální fázi, nejsou považovány za stejné:
Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
False
Process fidelity
Operators lze také porovnávat pomocí funkce process_fidelity z modulu Quantum Information. Jde o informačně-teoretickou veličinu vyjadřující, jak blízko jsou si dva kvantové kanály, a v případě unitárních operátorů nezávisí na globální fázi.
# 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
Všimni si, že process fidelity je obecně platnou mírou blízkosti pouze tehdy, pokud jsou vstupní operátory unitární (nebo CP v případě kvantových kanálů), a pokud vstupy nejsou CP, je vyvolána výjimka.
Další kroky
- Prozkoumej referenci Operator API.