Vai al contenuto principale

Input e output di Estimator

Versioni dei pacchetti

Il codice in questa pagina è stato sviluppato con i seguenti requisiti. Ti consigliamo di utilizzare queste versioni o versioni più recenti.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1

Questa pagina fornisce una panoramica degli input e degli output della primitiva Qiskit Runtime Estimator, che esegue carichi di lavoro su risorse di calcolo IBM Quantum®. Estimator ti consente di definire in modo efficiente carichi di lavoro vettorizzati usando una struttura dati chiamata Primitive Unified Bloc (PUB). Vengono usati come input per il metodo run() della primitiva Estimator, che esegue il carico di lavoro definito come job. Poi, dopo che il job è stato completato, i risultati vengono restituiti in un formato che dipende sia dai PUB usati che dalle opzioni di runtime specificate dalla primitiva.

Input

Ogni PUB ha questo formato:

(<single circuit>, <one or more observables>, <optional one or more parameter values>, <optional precision>),

I parameter values opzionali possono essere un elenco o un singolo parametro. Gli elementi degli osservabili e dei valori dei parametri vengono combinati seguendo le regole di broadcasting di NumPy come descritto nell'argomento Input e output delle primitive, e viene restituita una stima del valore di aspettazione per ogni elemento della forma broadcast.

info

Se l'input contiene misurazioni, vengono ignorate.

Per la primitiva Estimator, un PUB può contenere al massimo quattro valori:

  • Un singolo QuantumCircuit, che può contenere uno o più oggetti Parameter
  • Un elenco di uno o più osservabili, che specificano i valori di aspettazione da stimare, disposti in un array (ad esempio, un singolo osservabile rappresentato come array 0-d, un elenco di osservabili come array 1-d e così via). I dati possono essere in uno qualsiasi dei formati ObservablesArrayLike come Pauli, SparsePauliOp, PauliList o str.
    Osservabili che commutano
    • Gli osservabili che commutano nello stesso PUB vengono raggruppati usando questo metodo.
    • Gli osservabili che commutano in PUB diversi, anche se hanno lo stesso Circuit, non vengono stimati usando la stessa misurazione. Ogni PUB rappresenta una base diversa per la misurazione e quindi sono necessarie misurazioni separate per ogni PUB.
    • Per assicurarti che gli osservabili che commutano siano stimati usando la stessa misurazione, raggruppali nello stesso PUB.
  • Una raccolta di valori dei parametri con cui associare il circuito. Questo può essere specificato come un singolo oggetto array-like in cui l'ultimo indice è sugli oggetti Parameter del circuito o omesso (o equivalentemente, impostato su None) se il circuito non ha oggetti Parameter.
  • (Opzionalmente) Una precisione target per i valori di aspettazione da stimare

Il seguente codice dimostra un esempio di set vettorizzati di input alla primitiva Estimator e li esegue su un backend IBM® come singolo oggetto RuntimeJobV2.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
from qiskit.circuit import (
Parameter,
QuantumCircuit,
)
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp

from qiskit_ibm_runtime import (
QiskitRuntimeService,
EstimatorV2 as Estimator,
)

import numpy as np

# Instantiate runtime service and get
# the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Define a circuit with two parameters.
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.ry(Parameter("a"), 0)
circuit.rz(Parameter("b"), 0)
circuit.cx(0, 1)
circuit.h(0)

# Transpile the circuit
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
transpiled_circuit = pm.run(circuit)
layout = transpiled_circuit.layout

# Now define a sweep over parameter values, the last axis of dimension 2 is
# for the two parameters "a" and "b"
params = np.vstack(
[
np.linspace(-np.pi, np.pi, 100),
np.linspace(-4 * np.pi, 4 * np.pi, 100),
]
).T

# Define three observables. The inner length-1 lists cause this array of
# observables to have shape (3, 1), rather than shape (3,) if they were
# omitted.
observables = [
[SparsePauliOp(["XX", "IY"], [0.5, 0.5])],
[SparsePauliOp("XX")],
[SparsePauliOp("IY")],
]
# Apply the same layout as the transpiled circuit.
observables = [
[observable.apply_layout(layout) for observable in observable_set]
for observable_set in observables
]

# Estimate the expectation value for all 300 combinations of observables
# and parameter values, where the pub result will have shape (3, 100).
#
# This shape is due to our array of parameter bindings having shape
# (100, 2), combined with our array of observables having shape (3, 1).
estimator_pub = (transpiled_circuit, observables, params)

# Instantiate the new Estimator object, then run the transpiled circuit
# using the set of parameters and observables.
estimator = Estimator(mode=backend)
job = estimator.run([estimator_pub])
result = job.result()

Output

Dopo che uno o più PUB vengono inviati a una QPU per l'esecuzione e un job viene completato con successo, i dati vengono restituiti come oggetto contenitore PrimitiveResult accessibile chiamando il metodo RuntimeJobV2.result().

Il PrimitiveResult contiene un elenco iterabile di oggetti PubResult che contengono i risultati di esecuzione per ogni PUB.

Ogni elemento di questo elenco corrisponde a ogni PUB inviato al metodo run() della primitiva (ad esempio, un job inviato con 20 PUB restituirà un oggetto PrimitiveResult che contiene un elenco di 20 oggetti PubResult, uno corrispondente a ogni PUB).

Ogni PubResult per la primitiva Estimator contiene almeno un array di valori di aspettazione (PubResult.data.evs) e deviazioni standard associate (sia PubResult.data.stds che PubResult.data.ensemble_standard_error a seconda del resilience_level usato), ma può contenere più dati a seconda delle opzioni di mitigazione degli errori specificate.

Ogni oggetto PubResult possiede sia un attributo data che un attributo metadata.

  • L'attributo data è un DataBin personalizzato che contiene i valori di misurazione effettivi, le deviazioni standard e così via.
  • Il DataBin ha vari attributi a seconda della forma o struttura del PUB associato e delle opzioni di mitigazione degli errori specificate dalla primitiva usata per inviare il job (ad esempio, ZNE o PEC).
  • L'attributo metadata contiene informazioni sulle opzioni di runtime e mitigazione degli errori usate (spiegate più avanti nella sezione Metadati del risultato di questa pagina).

Di seguito è riportato uno schema visivo della struttura dati PrimitiveResult per l'output di Estimator:

└── PrimitiveResult
├── PubResult[0]
│ ├── metadata
│ └── data ## In the form of a DataBin object
│ ├── evs
│ │ └── List of estimated expectation values in the shape
| | specified by the first pub
│ └── stds
│ └── List of calculated standard deviations in the
| same shape as above
├── PubResult[1]
| ├── metadata
| └── data ## In the form of a DataBin object
| ├── evs
| │ └── List of estimated expectation values in the shape
| | specified by the second pub
| └── stds
| └── List of calculated standard deviations in the
| same shape as above
├── ...
├── ...
└── ...

In breve, un singolo job restituisce un oggetto PrimitiveResult e contiene un elenco di uno o più oggetti PubResult. Questi oggetti PubResult memorizzano quindi i dati di misurazione per ogni PUB inviato al job.

Il seguente frammento di codice descrive il formato PrimitiveResult (e il relativo PubResult) per il job creato sopra.

print(
f"The result of the submitted job had {len(result)} "
f"PUBs and has a value:\n {result}\n"
)
print(
"The associated PubResult of this job has the following data bins:\n "
"{result[0].data}\n"
)
print(f"And this DataBin has attributes: {result[0].data.keys()}")
print(
"Recall that this shape is due to our array of parameter binding sets"
"having shape (100, 2), where 2 is the number of parameters in the "
"circuit, combined with our array of observables having shape (3, 1). \n"
)
with np.printoptions(threshold=200):
print(
"The expectation values measured from this PUB are: \n"
"{result[0].data.evs}\n"
)
The result of the submitted job had 1 PUB and has a value:
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(3, 100), dtype=float64>), stds=np.ndarray(<shape=(3, 100), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 100), dtype=float64>), shape=(3, 100)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

The associated PubResult of this job has the following data bins:
DataBin(evs=np.ndarray(<shape=(3, 100), dtype=float64>), stds=np.ndarray(<shape=(3, 100), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 100), dtype=float64>), shape=(3, 100))

And this DataBin has attributes: dict_keys(['evs', 'stds', 'ensemble_standard_error'])
Recall that this shape is due to our array of parameter binding sets having shape (100, 2) -- where 2 is the
number of parameters in the circuit -- combined with our array of observables having shape (3, 1).

The expectation values measured from this PUB are:
[[-0.00369065 0.15107692 0.30110431 ... -0.30159536 -0.15431523
0.00576586]
[ 0.00601655 0.04412133 0.1253447 ... -0.12434194 -0.04662823
0.01153171]
[-0.01339784 0.2580325 0.47686391 ... -0.47884878 -0.26200223
0. ]]

Come la primitiva Estimator calcola l'errore

Oltre alla stima della media degli osservabili passati nei PUB di input (il campo evs del DataBin), Estimator tenta anche di fornire una stima dell'errore associato a quei valori di aspettazione. Tutte le query di Estimator popolano il campo stds con una quantità come l'errore standard della media per ogni valore di aspettazione, ma alcune opzioni di mitigazione degli errori producono informazioni aggiuntive, come ensemble_standard_error.

Considera un singolo osservabile O\mathcal{O}. In assenza di ZNE, puoi pensare a ogni shot dell'esecuzione di Estimator come a una stima puntuale del valore di aspettazione O\langle \mathcal{O} \rangle. Se le stime puntuali sono in un vettore Os, allora il valore restituito in ensemble_standard_error è equivalente al seguente (in cui σO\sigma_{\mathcal{O}} è la deviazione standard della stima del valore di aspettazione e NshotsN_{shots} è il numero di shot):

σONshots,\frac{ \sigma_{\mathcal{O}} }{ \sqrt{N_{shots}} },

che tratta tutti gli shot come parte di un singolo ensemble. Se hai richiesto il twirling dei Gate (twirling.enable_gates = True), puoi ordinare le stime puntuali di O\langle \mathcal{O} \rangle in set che condividono un twirl comune. Chiama questi set di stime O_twirls, e ce ne sono num_randomizations (numero di twirl). Poi stds è l'errore standard della media di O_twirls, come in

σONtwirls,\frac{ \sigma_{\mathcal{O}} }{ \sqrt{N_{twirls}} },

dove σO\sigma_{\mathcal{O}} è la deviazione standard di O_twirls e NtwirlsN_{twirls} è il numero di twirl. Quando non abiliti il twirling, stds e ensemble_standard_error sono uguali.

Se abiliti ZNE, allora gli stds descritti sopra diventano pesi in una regressione non lineare a un modello di estrapolatore. Ciò che alla fine viene restituito negli stds in questo caso è l'incertezza del modello di fit valutata a un fattore di rumore zero. Quando c'è un fit scarso, o una grande incertezza nel fit, gli stds riportati possono diventare molto grandi. Quando ZNE è abilitato, vengono popolati anche pub_result.data.evs_noise_factors e pub_result.data.stds_noise_factors, in modo che tu possa fare la tua estrapolazione.

Metadati del risultato

Oltre ai risultati dell'esecuzione, sia gli oggetti PrimitiveResult che PubResult contengono un attributo di metadati sul job inviato. I metadati contenenti informazioni per tutti i PUB inviati (come le varie opzioni di runtime disponibili) si trovano in PrimitiveResult.metadata, mentre i metadati specifici per ogni PUB si trovano in PubResult.metadata.

nota

Nel campo dei metadati, le implementazioni delle primitive possono restituire qualsiasi informazione sull'esecuzione che è rilevante per loro, e non ci sono coppie chiave-valore garantite dalla primitiva base. Pertanto, i metadati restituiti potrebbero essere diversi nelle diverse implementazioni delle primitive.

# Print out the results metadata
print("The metadata of the PrimitiveResult is:")
for key, val in result.metadata.items():
print(f"'{key}' : {val},")

print("\nThe metadata of the PubResult result is:")
for key, val in result[0].metadata.items():
print(f"'{key}' : {val},")
The metadata of the PrimitiveResult is:
'dynamical_decoupling' : {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'},
'twirling' : {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'},
'resilience' : {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False},
'version' : 2,

The metadata of the PubResult result is:
'shots' : 4096,
'target_precision' : 0.015625,
'circuit_metadata' : {},
'resilience' : {},
'num_randomizations' : 32,