La classe Operator
Versioni dei pacchetti
Il codice in questa pagina è stato sviluppato utilizzando i seguenti requisiti. Si consiglia di usare queste versioni o versioni più recenti.
qiskit[all]~=2.3.0
Questa pagina mostra come usare la classe Operator. Per una panoramica di alto livello delle rappresentazioni degli operatori in Qiskit, inclusa la classe Operator e altre, consulta Panoramica delle classi operatore.
# 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
Convertire classi in Operator
Molte altre classi di Qiskit possono essere convertite direttamente in un oggetto Operator usando il metodo di inizializzazione dell'operatore. Per esempio:
- Oggetti
Pauli - Oggetti
GateeInstruction - Oggetti
QuantumCircuit
Tieni presente che quest'ultimo punto significa che puoi usare la classe Operator come simulatore unitario per calcolare la matrice unitaria finale di un circuito quantistico, senza dover invocare un backend di simulazione. Se il circuito contiene operazioni non supportate, viene sollevata un'eccezione. Le operazioni non supportate sono: misura, reset, operazioni condizionali o un gate che non possiede una definizione matriciale né una decomposizione in termini di gate con definizione matriciale.
# 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))
Usare gli Operator nei circuiti
Gli Operator unitari possono essere inseriti direttamente in un QuantumCircuit usando il metodo QuantumCircuit.append. Questo converte l'Operator in un oggetto UnitaryGate, che viene aggiunto al circuito.
Se l'operatore non è unitario, viene sollevata un'eccezione. Puoi verificarlo con la funzione Operator.is_unitary(), che restituisce True se l'operatore è unitario e False altrimenti.
# 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")
Nota che nell'esempio precedente l'operatore viene inizializzato da un oggetto Pauli. Tuttavia, l'oggetto Pauli può anche essere inserito direttamente nel circuito e verrà convertito in una sequenza di gate Pauli a qubit singolo:
# 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
Combinare gli Operator
Gli operatori possono essere combinati in diversi modi.
Prodotto tensoriale
Due operatori e possono essere combinati in un operatore prodotto tensoriale usando la funzione Operator.tensor. Tieni presente che se sia che sono operatori a qubit singolo, allora A.tensor(B) = avrà i sottosistemi indicizzati con la matrice sul sottosistema 0 e la matrice sul sottosistema 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))
Espansione tensoriale
Un'operazione strettamente correlata è Operator.expand, che si comporta come il prodotto tensoriale ma in ordine inverso. Pertanto, per due operatori e si ha A.expand(B) = , con i sottosistemi indicizzati con la matrice sul sottosistema 0 e la matrice sul sottosistema 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))
Composizione
Puoi anche comporre due operatori e per implementare la moltiplicazione matriciale usando il metodo Operator.compose. A.compose(B) restituisce l'operatore con matrice :
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,))
Puoi anche comporre in ordine inverso applicando davanti ad usando il kwarg front di 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,))
Composizione su sottosistemi
Tieni presente che la composizione precedente richiede che la dimensione di output totale del primo operatore sia uguale alla dimensione di input totale dell'operatore composto (e analogamente, la dimensione di output di deve essere uguale alla dimensione di input di quando si compone con front=True).
Puoi anche comporre un operatore più piccolo con una selezione di sottosistemi di un operatore più grande usando il kwarg qargs di compose, con o senza front=True. In questo caso, le dimensioni di input e output rilevanti dei sottosistemi coinvolti nella composizione devono corrispondere. Nota che l'operatore più piccolo deve essere sempre l'argomento del metodo compose.
Per esempio, per comporre un gate a due qubit con un operatore a tre qubit:
# 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))
Combinazioni lineari
Gli operatori possono essere combinati anche usando le operazioni lineari standard: addizione, sottrazione e moltiplicazione scalare per numeri complessi.
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))
Un punto importante è che mentre tensor, expand e compose preservano l'unitarietà degli operatori unitari, le combinazioni lineari non lo fanno; pertanto, sommando due operatori unitari si otterrà in generale un operatore non unitario:
op.is_unitary()
False
Conversione implicita in Operator
Tieni presente che per tutti i metodi seguenti, se il secondo oggetto non è già un oggetto Operator, viene convertito implicitamente in uno dal metodo. Ciò significa che le matrici possono essere passate direttamente senza essere esplicitamente convertite in un Operator prima. Se la conversione non è possibile, viene sollevata un'eccezione.
# 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,))
Confrontare gli Operator
Gli operatori implementano un metodo di uguaglianza che può essere usato per verificare se due operatori sono approssimativamente uguali.
Operator(Pauli("X")) == Operator(XGate())
True
Tieni presente che questo controlla che ogni elemento matriciale degli operatori sia approssimativamente uguale; due unitari che differiscono per una fase globale non sono considerati uguali:
Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())
False
Fedeltà di processo
Puoi anche confrontare gli operatori usando la funzione process_fidelity del modulo Quantum Information. Si tratta di una quantità di teoria dell'informazione che misura quanto due canali quantistici sono vicini tra loro, e nel caso degli operatori unitari non dipende dalla fase globale.
# 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
Tieni presente che la fedeltà di processo è generalmente una misura di prossimità valida solo se gli operatori di input sono unitari (o CP nel caso di canali quantistici), e viene sollevata un'eccezione se gli input non sono CP.
Passi successivi
- Esplora il riferimento all'API Operator.