Primitivi
qiskit.primitives
Le primitive sono blocchi computazionali da utilizzare in applicazioni più grandi le cui unità di input, chiamate blocchi primitivi unificati (PUB), richiedono risorse quantistiche per produrre in modo efficiente gli output.
Attualmente esistono due tipi di primitive le cui astrazioni, nelle loro ultime versioni, sono definite da BaseSamplerV2 e BaseEstimatorV2. I campionatori hanno il compito di accettare circuiti quantistici (o passate di valori su circuiti parametrizzati) e di effettuare il campionamento dai relativi registri di uscita classici. Gli stimatori accettano combinazioni di circuiti e grandezze osservabili (o relative scansioni) per stimare i valori attesi delle grandezze osservabili.
Qiskit offre un'implementazione di riferimento per ciascuna di queste astrazioni nelle StatevectorSampler classi e StatevectorEstimator .
Le versioni precedenti delle astrazioni relative al campionatore e allo stimatore sono definite da BaseSamplerV1 e BaseEstimatorV1. Queste interfacce seguono un formato di input-output diverso e meno flessibile per il run metodo e, nella pratica, sono state in gran parte sostituite da BaseSamplerV2 e BaseEstimatorV2. Tuttavia, le definizioni originali delle interfacce astratte sono state mantenute per garantire la compatibilità con le versioni precedenti. Consulta la sezione dedicata alla migrazione in questa pagina per ulteriori dettagli sulla differenza tra V1 e V2.
Panoramica dell' EstimatorV2
BaseEstimatorV2 è una funzione primitiva che stima i valori attesi per le combinazioni di circuiti quantistici e osservabili fornite.
Al termine della costruzione, si utilizza un estimatore chiamando il suo run() metodo con un elenco di pub (Primitive Unified Blocs). Ogni voce contiene tre valori che, insieme, definiscono un'unità di lavoro di calcolo che lo stimatore deve completare:
- un singolo elemento
QuantumCircuit, eventualmente parametrizzato, il cui stato finale definiamo come , - una o più grandezze osservabili (specificate come qualsiasi
ObservablesArrayLike, tra cuiPauli,SparsePauliOp,str) che indicano quali valori attesi stimare, indicate c e - una raccolta di insiemi di valori dei parametri a cui associare il circuito, .
L'esecuzione di uno stimatore restituisce un BasePrimitiveJob oggetto, in cui la chiamata al metodo result() fornisce stime dei valori attesi e metadati per ciascun pub:
Le osservabili e i valori dei parametri di un pub possono essere valutati ad array con dimensioni arbitrarie, dove si applicano le regole di trasmissione standard, in modo che, a sua volta, anche il risultato stimato per ogni pub sia in generale valutato ad array. Per ulteriori informazioni, consultare il sito.
Ecco un esempio di utilizzo di uno stimatore.
from qiskit.primitives import StatevectorEstimator as Estimator
from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp
psi1 = RealAmplitudes(num_qubits=2, reps=2)
psi2 = RealAmplitudes(num_qubits=2, reps=3)
H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
H2 = SparsePauliOp.from_list([("IZ", 1)])
H3 = SparsePauliOp.from_list([("ZI", 1), ("ZZ", 1)])
theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 1, 2, 3, 5, 8, 13]
theta3 = [1, 2, 3, 4, 5, 6]
estimator = Estimator()
# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
job = estimator.run([(psi1, H1, [theta1])])
job_result = job.result() # It will block until the job finishes.
print(f"The primitive-job finished with result {job_result}")
# calculate [ [<psi1(theta1)|H1|psi1(theta1)>,
# <psi1(theta3)|H3|psi1(theta3)>],
# [<psi2(theta2)|H2|psi2(theta2)>] ]
job2 = estimator.run(
[
(psi1, [H1, H3], [theta1, theta3]),
(psi2, H2, theta2)
],
precision=0.01
)
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")Panoramica dell' SamplerV2
BaseSamplerV2 è una primitiva che campiona gli output dei circuiti quantistici.
Al termine della costruzione, si utilizza un campionatore richiamando il suo run() metodo con un elenco di pub (Primitive Unified Blocs). Ogni pub contiene valori che, nel loro insieme, definiscono un'unità di lavoro computazionale che il campionatore deve completare:
- Un singolo
QuantumCircuiteventualmente parametrizzato. - Un insieme di valori di parametri a cui legare il circuito se è parametrico.
- Opzionalmente, il numero di scatti da campionare, determinato nel metodo di corsa se non impostato.
L'esecuzione di un sampler restituisce un BasePrimitiveJob oggetto, in cui la chiamata al metodo result() genera campioni di output e metadati per ogni pub.
Ecco un esempio di utilizzo di un campionatore.
from qiskit.primitives import StatevectorSampler as Sampler
from qiskit import QuantumCircuit
from qiskit.circuit.library import RealAmplitudes
# create a Bell circuit
bell = QuantumCircuit(2)
bell.h(0)
bell.cx(0, 1)
bell.measure_all()
# create two parameterized circuits
pqc = RealAmplitudes(num_qubits=2, reps=2)
pqc.measure_all()
pqc2 = RealAmplitudes(num_qubits=2, reps=3)
pqc2.measure_all()
theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 2, 3, 4, 5, 6, 7]
# initialization of the sampler
sampler = Sampler()
# collect 128 shots from the Bell circuit
job = sampler.run([bell], shots=128)
job_result = job.result()
print(f"The primitive-job finished with result {job_result}")
# run a sampler job on the parameterized circuits
job2 = sampler.run([(pqc, theta1), (pqc2, theta2)])
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")Panoramica dell' EstimatorV1
Al momento non esistono implementazioni dell'interfaccia legacy EstimatorV1 in Qiskit. Tuttavia, la definizione dell'interfaccia astratta contenuta in BaseEstimatorV1 fa ancora parte del pacchetto per garantire la retrocompatibilità con le implementazioni esterne.
Un'implementazione EstimatorV1 viene inizializzata con un insieme di parametri vuoto. BaseEstimatorV1 può essere richiamato tramite il .run() metodo con i seguenti parametri:
- circuiti quantistici ( ): elenco di circuiti quantistici (parametrizzati) (un elenco di oggetti)
QuantumCircuitoggetti). - observables ( ): un elenco di
SparsePauliOpoggetti. - valori dei parametri ( ): elenco di insiemi di valori da legare ai parametri dei circuiti quantistici (elenco di elenchi di float).
Il metodo dovrebbe restituire un JobV1 oggetto. L'esecuzione di questa funzione qiskit.providers.JobV1.result() restituisce un elenco di valori attesi, oltre a metadati opzionali quali gli intervalli di confidenza relativi alla stima.
Ecco un esempio di utilizzo di un'implementazione di EstimatorV1 . Si noti che attualmente non esistono implementazioni dell'interfaccia legacy EstimatorV1 in Qiskit.
# This is a fictional import path.
# There are currently no EstimatorV1 implementations in Qiskit.
from estimator_v1_location import EstimatorV1
from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp
psi1 = RealAmplitudes(num_qubits=2, reps=2)
psi2 = RealAmplitudes(num_qubits=2, reps=3)
H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
H2 = SparsePauliOp.from_list([("IZ", 1)])
H3 = SparsePauliOp.from_list([("ZI", 1), ("ZZ", 1)])
theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 1, 2, 3, 5, 8, 13]
theta3 = [1, 2, 3, 4, 5, 6]
estimator = EstimatorV1()
# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
job = estimator.run([psi1], [H1], [theta1])
job_result = job.result() # It will block until the job finishes.
print(f"The primitive-job finished with result {job_result}")
# calculate [ <psi1(theta1)|H1|psi1(theta1)>,
# <psi2(theta2)|H2|psi2(theta2)>,
# <psi1(theta3)|H3|psi1(theta3)> ]
job2 = estimator.run(
[psi1, psi2, psi1],
[H1, H2, H3],
[theta1, theta2, theta3]
)
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")Panoramica dell' SamplerV1
Al momento non esistono implementazioni dell'interfaccia legacy SamplerV1 in Qiskit. Tuttavia, la definizione dell'interfaccia astratta contenuta in BaseSamplerV1 fa ancora parte del pacchetto per garantire la retrocompatibilità con le implementazioni esterne.
Le classi di campionatori calcolano le probabilità o le quasi-probabilità delle stringhe di bit dei circuiti quantistici.
A SamplerV1 viene inizializzato con un insieme di parametri vuoto. BaseSamplerV1 Le implementazioni possono essere richiamate tramite il .run() metodo con i seguenti parametri:
- circuiti quantistici ( ): elenco di circuiti quantistici (parametrizzati). (un elenco di
QuantumCircuitoggetti) - valori dei parametri ( ): elenco di insiemi di valori dei parametri da legare ai parametri dei circuiti quantistici. (elenco di elenchi di galleggianti)
.run() restituirà un JobV1 oggetto. La chiamata qiskit.providers.JobV1.result() restituisce un SamplerResult oggetto che contiene le probabilità o le quasi-probabilità delle stringhe di bit, oltre a metadati opzionali quali le barre di errore nei campioni.
Ecco un esempio di utilizzo dell'implementazione di SamplerV1 . Si noti che attualmente non esistono implementazioni dell'interfaccia legacy SamplerV1 in Qiskit.
# This is a fictional import path.
# There are currently no SamplerV1 implementations in Qiskit.
from sampler_v1_location import Sampler
from qiskit import QuantumCircuit
from qiskit.circuit.library import RealAmplitudes
# a Bell circuit
bell = QuantumCircuit(2)
bell.h(0)
bell.cx(0, 1)
bell.measure_all()
# two parameterized circuits
pqc = RealAmplitudes(num_qubits=2, reps=2)
pqc.measure_all()
pqc2 = RealAmplitudes(num_qubits=2, reps=3)
pqc2.measure_all()
theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 2, 3, 4, 5, 6, 7]
# initialization of the sampler
sampler = SamplerV1()
# Sampler runs a job on the Bell circuit
job = sampler.run(
circuits=[bell], parameter_values=[[]], parameters=[[]]
)
job_result = job.result()
print([q.binary_probabilities() for q in job_result.quasi_dists])
# Sampler runs a job on the parameterized circuits
job2 = sampler.run(
circuits=[pqc, pqc2],
parameter_values=[theta1, theta2],
parameters=[pqc.parameters, pqc2.parameters])
job_result = job2.result()
print([q.binary_probabilities() for q in job_result.quasi_dists])Migrazione da Primitives V1 a V2
La differenza formale tra le API delle primitive V1 e V2 risiede nelle classi base da cui derivano le implementazioni delle primitive, tutte elencate in fondo alla pagina. A livello concettuale, tuttavia, ecco alcune differenze significative da tenere presenti quando si esegue la migrazione da V1 a V2:
-
Le primitive di V2 favoriscono gli ingressi vettoriali, dove i singoli circuiti possono essere raggruppati con specifiche di valore vettoriale (o più in generale di array). Ogni gruppo è chiamato blocco unificato primitivo (pub) e ogni pub ottiene il proprio risultato. Ad esempio, nello stimatore è possibile confrontare le seguenti differenze:
# Favoured V2 pattern. There is only one pub here, but there could be more. job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])]) evs = job.result()[0].data.evs # V1 equivalent, where the same circuit must be provided four times. job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4]) evs = job.result().valuesNell'esempio precedente non è stato mostrato, per brevità, che il circuito può essere parametrico, con array di set di valori di parametri trasmessi contro l'array di osservabili. Il campionatore è simile, ma senza osservabili:
# Favoured V2 pattern. There is only one pub here, but there could be more. job = sampler_v2.run([(circuit, [vals1, vals2, vals3])]) samples = job.result()[0].data # V1 equivalent, where the same circuit must be provided three times. sampler_v1.run([circuit] * 3, [vals1, vals2, vals3]) quasi_dists = job.result().quasi_dists -
Il campionatore V2 restituisce campioni di risultati classici, conservando l'ordine di scatto in cui sono stati misurati. Ciò è in contrasto con il campionatore V1 che produce distribuzioni di quasi-probabilità che sono invece una stima della distribuzione degli esiti classici. Inoltre, gli oggetti risultato del campionatore V2 organizzano i dati in termini di nomi dei registri classici dei circuiti di ingresso, il che offre una naturale compatibilità con i circuiti dinamici.
L'analogo più vicino alle distribuzioni di quasi-probabilità nell'interfaccia
V2è ilget_counts()metodo illustrato nell'esempio riportato di seguito. Tuttavia, teniamo a sottolineare che, nel caso di esperimenti su scala industriale (100+ qubit), le probabilità di misurare due volte la stessa stringa di bit sono minime, per cui raggruppare i conteggi in un formato a dizionario non rappresenta in genere una strategia efficiente per l’elaborazione dei dati.circuit = QuantumCircuit(QuantumRegister(2, "qreg"), ClassicalRegister(2, "alpha")) circuit.h(0) circuit.cx(0, 1) circuit.measure([0, 1], [0, 1]) # V1 sampler usage result = sampler_v1.run([circuit]).result() quasi_dist = result.quasi_dists[0] # V2 sampler usage result = sampler_v2.run([circuit]).result() # these are the bit values from the alpha register, over all shots bitvals = result[0].data.alpha # we can use it to generate a Counts mapping, which is similar to a quasi prob distribution counts = bitvals.get_counts() # which can in turn be converted to the V1 type through normalization quasi_dist = QuasiDistribution({outcome: freq / shots for outcome, freq in counts.items()}) -
Le primitive dell' V2 hanno portato il concetto di overhead di campionamento, intrinseco a tutti i sistemi quantistici a causa della loro natura probabilistica, fuori dalle opzioni e direttamente nell'API stessa. Per il sampler, ciò significa che l'argomento
shotsfa ora parte dellarun()firma e, inoltre, che ogni pub è in grado di specificare il proprio valore pershots, il quale ha la precedenza su qualsiasi valore assegnato al metodo. Lo stimatore dispone di un argomento analogoprecisionche specifica le barre di errore a cui l'implementazione primitiva dovrebbe puntare per le stime del valore atteso.Questo concetto non è presente nell'API delle primitive di V1, anche se tutte le implementazioni delle primitive di V1 hanno impostazioni relative da qualche parte nelle loro opzioni.
# Sample two circuits at 128 shots each. sampler_v2.run([circuit1, circuit2], shots=128) # Sample two circuits at different amounts of shots. The "None"s are necessary as placeholders # for the lack of parameter values in this example. sampler_v2.run([(circuit1, None, 123), (circuit2, None, 456)]) # Estimate expectation values for two pubs, both with 0.05 precision. estimator_v2.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)
API Primitive
Parametri V2
ParameterLike | Rappresentare un tipo di unione |
BindingsArray( [dati, forma] ) | Memorizza i set di valori di associazione dei parametri per un qiskit.QuantumCircuit. |
BindingsArrayLike | Alias di `Mapping[ParameterLike |
Stima V2
BaseEstimatorV2() | Classe base per le implementazioni di EstimatorV2 . |
StatevectorEstimator(*[, precisione_predefinita,...] ) | Implementazione semplice di BaseEstimatorV2 con simulazione completa del vettore di stato. |
BackendEstimatorV2(*, backend[, opzioni] ) | Valuta i valori di aspettativa per le combinazioni di circuiti quantistici e osservabili fornite. |
EstimatorPub(circuito, grandezze osservabili[,...] ) | Blocco unificato primitivo per qualsiasi primitiva Estimator. |
ObservablesArray(osservabili[, num_qubit,...] ) | Un array ND di osservabili ermitiani per un Estimator primitivo. |
ObservableLike | Rappresentare un tipo di unione |
EstimatorPubLike | alias di EstimatorPub |
ObservablesArrayLike | Alias di `ObservableLike |
Campionatore V2
BaseSamplerV2() | Classe base per le implementazioni di SamplerV2 . |
StatevectorSampler(*[, default_shots, seed] ) | Semplice implementazione tramite BaseSamplerV2 simulazione con vettore di stato completo. |
BackendSamplerV2(*, backend[, opzioni] ) | Valuta le stringhe di bit per i circuiti quantistici forniti |
SamplerPub(circuito[, valori_dei_parametri,...] ) | Pub (Primitive Unified Bloc) per un campionatore. |
SamplerPubLike | alias di SamplerPub |
Risultati V2
BitArray(array, num_bits) | Memorizza un array di valori di bit. |
DataBin(*[, forma] ) | I dati principali provengono da un singolo pub su PubResult. |
PrimitiveResult(pub_results[, metadata] ) | Un contenitore per risultati multipli di pub e metadati globali. |
PubResult(dati[, metadati] ) | L'oggetto risultato per un singolo pub (blocco unificato primitivo). |
SamplerPubResult(dati[, metadati] ) | Risultato di Sampler Pub. |
BasePrimitiveJob(job_id, **kwargs) | Classe base astratta del lavoro primitivo. |
PrimitiveJob(function, *args, **kwargs) | Gestisci un lavoro dalle implementazioni di riferimento delle primitive in Qiskit. |
Stima V1
BaseEstimatorV1(*[, opzioni] ) | Classe base per le implementazioni di EstimatorV1 . |
EstimatorResult(valori, metadati) | Risultato dello stimatore V1. |
Campionatore V1
BaseSamplerV1(*[, opzioni] ) | Campionatore V1 classe base |
SamplerResult(quasi_dists, metadati) | Risultato del campionatore V1. |