Utilità per gli addon di Qiskit
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
qiskit-ibm-runtime~=0.43.1
qiskit-addon-utils~=0.3.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-utils qiskit-ibm-runtime
Il pacchetto delle utilità per gli addon di Qiskit è una raccolta di funzionalità per integrare i flussi di lavoro che coinvolgono uno o più addon di Qiskit. Ad esempio, questo pacchetto contiene funzioni per la creazione di Hamiltoniani, la generazione di circuiti di evoluzione temporale di Trotter, e il taglio e la combinazione di circuiti quantistici.
Installazione
Esistono due modi per installare le utilità per gli addon di Qiskit: PyPI e la compilazione dal sorgente. Si raccomanda di installare questi pacchetti in un ambiente virtuale per garantire la separazione tra le dipendenze dei pacchetti.
Installazione da PyPI
Il modo più diretto per installare il pacchetto delle utilità per gli addon di Qiskit è tramite PyPI.
pip install 'qiskit-addon-utils'
Installazione dal sorgente
Clicca qui per leggere come installare questo pacchetto manualmente.
Se desideri contribuire a questo pacchetto o vuoi installarlo manualmente, prima clona il repository:
git clone git@github.com:Qiskit/qiskit-addon-utils.git
e installa il pacchetto tramite pip. Se prevedi di eseguire i tutorial presenti nel repository del pacchetto, installa anche le dipendenze per i notebook. Se prevedi di sviluppare nel repository, installa le dipendenze dev.
pip install tox jupyterlab -e '.[notebook-dependencies,dev]'
Inizia a usare le utilità
Ci sono diversi moduli all'interno del pacchetto qiskit-addon-utils, tra cui uno per la generazione di problemi per la simulazione di sistemi quantistici, la colorazione di grafi per posizionare i gate in un circuito quantistico in modo più efficiente, e il taglio di circuiti, che può essere utile per la retropropagazione degli operatori. Le sezioni seguenti riassumono ciascun modulo. Anche la documentazione API del pacchetto contiene informazioni utili.
Generazione di problemi
Il contenuto del modulo qiskit_addon_utils.problem_generators comprende:
- Una funzione
generate_xyz_hamiltonian(), che genera una rappresentazioneSparsePauliOpconsapevole della connettività per un modello XYZ di tipo Ising:
- Una funzione
generate_time_evolution_circuit(), che costruisce un circuito che modella l'evoluzione temporale di un dato operatore. - Tre diversi oggetti
PauliOrderStrategyper enumerare i diversi ordinamenti delle stringhe di Pauli. Questo è particolarmente utile se usato insieme alla colorazione di grafi e può essere impiegato come argomento sia nella funzionegenerate_xyz_hamiltonian()che ingenerate_time_evolution_circuit().
Colorazione di grafi
Il modulo qiskit_addon_utils.coloring viene usato per colorare gli archi di una mappa di accoppiamento e sfruttare questa colorazione per posizionare i gate in un circuito quantistico in modo più efficiente. Lo scopo di questa mappa di accoppiamento con archi colorati è trovare un insieme di colori per gli archi tale che nessun arco dello stesso colore condivida un nodo comune. Per una QPU, questo significa che i gate lungo archi dello stesso colore (connessioni tra qubit) possono essere eseguiti contemporaneamente, rendendo il circuito più veloce.
Come rapido esempio, puoi usare la funzione auto_color_edges() per generare una colorazione degli archi per un circuito naive che esegue un CZGate lungo ogni connessione tra qubit. Il frammento di codice seguente utilizza la mappa di accoppiamento del backend FakeSherbrooke, crea questo circuito naive, poi usa la funzione auto_color_edges() per creare un circuito equivalente più efficiente.
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
from qiskit import QuantumCircuit
from qiskit_addon_utils.coloring import auto_color_edges
from qiskit_addon_utils.slicing import combine_slices, slice_by_depth
from collections import defaultdict
backend = FakeSherbrooke()
coupling_map = backend.coupling_map
# Create naive circuit
circuit = QuantumCircuit(backend.num_qubits)
for edge in coupling_map.graph.edge_list():
circuit.cz(edge[0], edge[1])
# Color the edges of the coupling map
coloring = auto_color_edges(coupling_map)
circuit_with_coloring = QuantumCircuit(backend.num_qubits)
# Make a reverse coloring dict in order to make the circuit
color_to_edge = defaultdict(list)
for edge, color in coloring.items():
color_to_edge[color].append(edge)
# Place edges in order of color
for edges in color_to_edge.values():
for edge in edges:
circuit_with_coloring.cz(edge[0], edge[1])
print(f"The circuit without using edge coloring has depth: {circuit.depth()}")
print(
f"The circuit using edge coloring has depth: {circuit_with_coloring.depth()}"
)
The circuit without using edge coloring has depth: 37
The circuit using edge coloring has depth: 3
Taglio
Infine, il modulo qiskit-addon-utils.slicing contiene funzioni e pass del transpiler per lavorare con la creazione di "slice" di circuiti, ovvero partizioni temporali di un QuantumCircuit che si estendono su tutti i qubit. Queste slice sono usate principalmente per la retropropagazione degli operatori. I quattro metodi principali con cui un circuito può essere tagliato sono: per tipo di gate, per profondità, per colorazione, o tramite istruzioni Barrier. L'output di queste funzioni di taglio restituisce una lista di oggetti QuantumCircuit. I circuiti tagliati possono anche essere ricombinati usando la funzione combine_slices(). Leggi il riferimento API del modulo per maggiori informazioni.
Di seguito sono riportati alcuni esempi di come creare queste slice utilizzando il seguente circuito:
import numpy as np
from qiskit import QuantumCircuit
num_qubits = 9
qc = QuantumCircuit(num_qubits)
qc.ry(np.pi / 4, range(num_qubits))
qubits_1 = [i for i in range(num_qubits) if i % 2 == 0]
qubits_2 = [i for i in range(num_qubits) if i % 2 == 1]
qc.cx(qubits_1[:-1], qubits_2)
qc.cx(qubits_2, qubits_1[1:])
qc.cx(qubits_1[-1], qubits_1[0])
qc.rx(np.pi / 4, range(num_qubits))
qc.rz(np.pi / 4, range(num_qubits))
qc.draw("mpl", scale=0.6)
Nel caso in cui non ci sia un modo chiaro per sfruttare la struttura di un circuito per la retropropagazione degli operatori, puoi partizionare il circuito in slice di una data profondità.
# Slice circuit into partitions of depth 1
slices = slice_by_depth(qc, 1)
# Recombine slices in order to visualize the partitions together
combined_slices = combine_slices(slices, include_barriers=True)
combined_slices.draw("mpl", scale=0.6)
In casi come l'esecuzione di circuiti di Trotter per modellare la dinamica di un sistema quantistico, potrebbe essere vantaggioso tagliare per tipo di gate.
from qiskit_addon_utils.slicing import slice_by_gate_types
slices = slice_by_gate_types(qc)
# Recombine slices in order to visualize the partitions together
combined_slices = combine_slices(slices, include_barriers=True)
combined_slices.draw("mpl", scale=0.6)
Se il tuo flusso di lavoro è progettato per sfruttare la connettività fisica dei qubit della QPU su cui verrà eseguito, puoi creare slice basate sulla colorazione degli archi. Il frammento di codice seguente assegnerà una 3-colorazione agli archi del circuito e taglierà il circuito rispetto alla colorazione degli archi. (Nota: questo influisce solo sui gate non locali. I gate a singolo qubit verranno tagliati per tipo di gate).
from qiskit_addon_utils.slicing import slice_by_coloring
# Assign a color to each set of connected qubits
coloring = {}
for i in range(num_qubits - 1):
coloring[(i, i + 1)] = i % 3
coloring[(num_qubits - 1, 0)] = 2
# Create a circuit with operations added in order of color
qc = QuantumCircuit(num_qubits)
qc.ry(np.pi / 4, range(num_qubits))
edges = [
edge for color in range(3) for edge in coloring if coloring[edge] == color
]
for edge in edges:
qc.cx(edge[0], edge[1])
qc.rx(np.pi / 4, range(num_qubits))
qc.rz(np.pi / 4, range(num_qubits))
# Create slices by edge color
slices = slice_by_coloring(qc, coloring=coloring)
# Recombine slices in order to visualize the partitions together
combined_slices = combine_slices(slices, include_barriers=True)
combined_slices.draw("mpl", scale=0.6)
Se hai una strategia di taglio personalizzata, puoi invece inserire delle barriere nel circuito per delimitare i punti di taglio e usare la funzione slice_by_barriers.
qc = QuantumCircuit(num_qubits)
qc.ry(np.pi / 4, range(num_qubits))
qc.barrier()
qubits_1 = [i for i in range(num_qubits) if i % 2 == 0]
qubits_2 = [i for i in range(num_qubits) if i % 2 == 1]
qc.cx(qubits_1[:-1], qubits_2)
qc.cx(qubits_2, qubits_1[1:])
qc.cx(qubits_1[-1], qubits_1[0])
qc.barrier()
qc.rx(np.pi / 4, range(num_qubits))
qc.rz(np.pi / 4, range(num_qubits))
qc.draw("mpl", scale=0.6)
Una volta inserite le barriere, puoi esaminare ciascuna slice singolarmente.
from qiskit_addon_utils.slicing import slice_by_barriers
slices = slice_by_barriers(qc)
slices[0].draw("mpl", scale=0.6)
slices[1].draw("mpl", scale=0.6)
slices[2].draw("mpl", scale=0.6)
Passi successivi
- Leggi la panoramica dell'addon OBP.
- Scopri come funziona l'addon SQD.
- Familiarizza con l'addon AQC-Tensor.