La classe Operator
Il codice di questa pagina è stato sviluppato in base ai seguenti requisiti. Si consiglia di utilizzare queste versioni o versioni più recenti.
qiskit[all]~=2.5.2
Questa pagina spiega come utilizzare la Operator classe. Per una panoramica generale sulle rappresentazioni degli operatori in Qiskit, tra cui la Operator classe e altre, consultare la sezione Panoramica delle classi di operatori.
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_fidelityConverti classi in operatori
Molte altre classi di Qiskit possono essere convertite direttamente in un oggetto Operator utilizzando il metodo di inizializzazione dell'operatore. Ad esempio:
PaulioggettiGatee gli oggetti diInstructionQuantumCircuitoggetti
Si noti che l'ultimo punto significa che si può usare la classe Operator come simulatore unitario per calcolare la matrice unitaria finale di un circuito quantistico, senza dover chiamare un backend simulatore. 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 ha una definizione di matrice o una decomposizione in termini di gate con definizioni di matrice.
# Create an Operator from a Pauli object
pauliXX = Pauli("XX")
Operator(pauliXX)Output:
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())Output:
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))Output:
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)Output:
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))
Utilizzo degli operatori nei circuiti
Unitario Operators può essere inserito direttamente in un QuantumCircuit utilizzando il metodo QuantumCircuit.append . Questo converte Operator in un oggetto UnitaryGate , che viene aggiunto al circuito.
Se l'operatore non è unitario, viene sollevata un'eccezione. Questo può essere verificato 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")Output:
Si noti che nell'esempio precedente l'operatore viene inizializzato da un oggetto Pauli . Tuttavia, l'oggetto Pauli può anche essere inserito direttamente nel circuito stesso e sarà convertito in una sequenza di porte Pauli a singolo qubit:
# Add to a circuit
circ2 = QuantumCircuit(2, 2)
circ2.append(Pauli("XX"), [0, 1])
circ2.measure([0, 1], [0, 1])
circ2.draw()Output:
┌────────────┐┌─┐
q_0: ┤0 ├┤M├───
│ Pauli(XX) │└╥┘┌─┐
q_1: ┤1 ├─╫─┤M├
└────────────┘ ║ └╥┘
c: 2/═══════════════╩══╩═
0 1
Operatori combinatori
Gli operatori possono essere combinati con diversi metodi.
Prodotto tensoriale
Due operatori e possono essere combinati in un operatore prodotto tensoriale utilizzando la funzione Operator.tensor . Si noti che se sia che sono operatori a singolo-qubit, allora A.tensor(B) = avrà i sottosistemi indicizzati come la matrice sul sottosistema 0 e la matrice sul sottosistema 1.
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.tensor(B)Output:
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 agisce come un prodotto tensoriale ma in ordine inverso. Quindi, per due operatori e si ha A.expand(B) = dove i sottosistemi sono indicizzati come la matrice sul sottosistema 0 e la matrice sul sottosistema 1.
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.expand(B)Output:
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
È anche possibile comporre due operatori e per implementare la moltiplicazione di matrici con il metodo Operator.compose . A.compose(B) restituisce l'operatore con la matrice :
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.compose(B)Output:
Operator([[ 0.+0.j, 1.+0.j],
[-1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))
Si può anche comporre in ordine inverso applicando davanti a usando il kwarg front di compose: A.compose(B, front=True) = :
A = Operator(Pauli("X"))
B = Operator(Pauli("Z"))
A.compose(B, front=True)Output:
Operator([[ 0.+0.j, -1.+0.j],
[ 1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))
Composizione del sottosistema
Si noti che la precedente composizione richiede che la dimensione totale di uscita del primo operatore sia uguale alla dimensione totale di ingresso dell'operatore composto (e analogamente, la dimensione di uscita di deve essere uguale alla dimensione di ingresso di quando si compone con front=True).
Si può anche comporre un operatore più piccolo con una selezione di sottosistemi su un operatore più grande, utilizzando il kwarg qargs di compose, con o senza front=True. In questo caso, le dimensioni di ingresso e di uscita dei sottosistemi composti devono corrispondere. *Si noti che l'operatore più piccolo deve essere sempre l'argomento dell' *compose metodo.
Ad 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])Output:
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)Output:
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 anche essere combinati utilizzando gli operatori lineari standard per l'addizione, la sottrazione e la moltiplicazione scalare per numeri complessi.
XX = Operator(Pauli("XX"))
YY = Operator(Pauli("YY"))
ZZ = Operator(Pauli("ZZ"))
op = 0.5 * (XX + YY - 3 * ZZ)
opOutput:
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; quindi, sommando due operatori unitari si otterrà, in generale, un operatore non unitario:
op.is_unitary()Output:
False
Conversione implicita in operatori
Si noti che per tutti i metodi seguenti, se il secondo oggetto non è già un oggetto Operator , viene implicitamente convertito in uno di essi dal metodo. Ciò significa che le matrici possono essere passate direttamente senza essere prima convertite esplicitamente in Operator . 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]])Output:
Operator([[0.+0.j, 1.+0.j],
[1.+0.j, 0.+0.j]],
input_dims=(2,), output_dims=(2,))
Confronta operatori
Gli operatori implementano un metodo di uguaglianza che può essere utilizzato per verificare se due operatori sono approssimativamente uguali.
Operator(Pauli("X")) == Operator(XGate())Output:
True
Si noti che in questo modo si verifica che ogni elemento della matrice degli operatori sia approssimativamente uguale; due unitari che differiscono di una fase globale non sono considerati uguali:
Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())Output:
False
Fedeltà del processo
È inoltre possibile confrontare gli operatori utilizzando la funzione process_fidelity del modulo Informazione quantistica. Si tratta di una quantità teorica dell'informazione che indica la vicinanza tra due canali quantistici e che, 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)Output:
Process fidelity = 1.0
Si noti che la fedeltà del processo è generalmente una misura valida di vicinanza solo se gli operatori in ingresso sono unitari (o CP nel caso di canali quantistici), e viene sollevata un'eccezione se gli ingressi non sono CP.
Passi successivi
- Esplorare il riferimento all' API dell'operatore.