Skip to main content
IBM Quantum Platform

Simulazione esatta con primitive dell' Qiskit SDK

  • 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.1
    

Le primitive di riferimento presenti nell' Qiskit SDK o eseguono simulazioni con vettori di stato locali. Queste simulazioni non consentono di modellare il rumore dei dispositivi, ma sono utili per realizzare rapidamente prototipi di algoritmi prima di passare a tecniche di simulazione più avanzate ( utilizzando Qiskit Aer ) o di eseguire gli algoritmi su dispositivi reali ( primitive IBM Quantum ).

La primitiva Estimator può calcolare i valori di aspettativa dei circuiti, mentre la primitiva Sampler può campionare dalle distribuzioni di uscita dei circuiti.

Le sezioni seguenti mostrano come utilizzare le primitive di riferimento per eseguire il flusso di lavoro a livello locale.


Utilizza lo strumento di stima di riferimento

L'implementazione di riferimento di EstimatorV2 in qiskit.primitives che viene eseguita su un simulatore locale di statevector è la classe StatevectorEstimator classe. Può ricevere in ingresso circuiti, osservabili e parametri e restituire i valori di aspettativa calcolati localmente.

Il codice seguente prepara i dati di input che verranno utilizzati negli esempi che seguono. Il tipo di input previsto per gli osservabili è qiskit.quantum_info.SparsePauliOp. Si noti che il circuito dell'esempio è parametrizzato, ma è possibile eseguire Estimator anche su circuiti non parametrizzati.

Note

Qualsiasi circuito passato a un Estimatore non deve includere alcuna misura.

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")

Output:

Output of the previous code cell
from qiskit.quantum_info import SparsePauliOp
import numpy as np

# observable(s) whose expected values you want to compute

observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])

# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
Transpile ai circuiti e agli osservabili ISA

Il flusso di lavoro delle primitive " IBM Quantum " richiede che i circuiti e gli osservabili vengano trasformati in modo da utilizzare esclusivamente istruzioni supportate dalla QPU (denominati "circuiti e osservabili con architettura del set di istruzioni " (ISA)). Le primitive di riferimento continuano ad accettare istruzioni astratte, poiché si basano su simulazioni con vettori di stato locali, ma la transpilazione del circuito potrebbe comunque rivelarsi vantaggiosa in termini di ottimizzazione del circuito stesso.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

Inizializza stimatore

Istanziare un qiskit.primitives.StatevectorEstimator.

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

Corri e ottieni risultati

Questo esempio utilizza solo un circuito (di tipo QuantumCircuit) e un osservabile.

Eseguire la stima chiamando il metodo StatevectorEstimator.run che restituisce un'istanza di un oggetto PrimitiveJob oggetto. È possibile ottenere i risultati del lavoro (come oggetto qiskit.primitives.PrimitiveResult oggetto) con il metodo qiskit.primitives.PrimitiveJob.result metodo.

job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")

Output:

 > Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>

Ottieni il valore atteso dal risultato

Il risultato delle primitive restituisce un array di PubResult oggetti, dove ogni elemento dell'array è un PubResult oggetto che contiene nei propri dati l'array delle valutazioni corrispondenti a ogni combinazione osservabile del circuito nell' PUB e.

Per recuperare i valori di aspettativa e i metadati per la prima (e in questo caso unica) valutazione del circuito, dobbiamo accedere alla valutazione data per PUB 0:

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

Output:

 > Expectation value: [4.         3.73205081 2.        ]
 > Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}

Imposta le opzioni di esecuzione di Estimator

Per impostazione predefinita, l'Estimatore di riferimento esegue un calcolo esatto del vettore di stato in base al valore di quantum_info.Statevector classe. Tuttavia, questo può essere modificato per introdurre l'effetto dell'overhead di campionamento (noto anche come "shot noise").

Estimator accetta un argomento precision che esprime le barre di errore che l'implementazione per le stime dei valori di aspettativa. Si tratta dell'overhead di campionamento ed è definito esclusivamente nel metodo .run() . In questo modo è possibile regolare con precisione l'opzione fino al livello PUB.

# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
    [(circuit, observable, parameter_values)], precision=0.05
)

Per un esempio completo, consulta la pagina degli esempi di Estimator.


Utilizza il campionatore di riferimento

L'implementazione di riferimento di SamplerV2 in qiskit.primitives è la classe StatevectorSampler classe. Prende in ingresso circuiti e parametri e restituisce i risultati del campionamento dalle distribuzioni di probabilità di uscita come una distribuzione di quasi-probabilità degli stati di uscita.

Il codice seguente prepara gli input utilizzati negli esempi che seguono. Si noti che questi esempi eseguono un singolo circuito parametrizzato, ma è possibile eseguire Sampler anche su circuiti non parametrizzati.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")

Output:

Output of the previous code cell
Note

Qualsiasi circuito quantistico passato a un campionatore deve includere misure.

Transpile ai circuiti e agli osservabili ISA

Il flusso di lavoro delle primitive " IBM Quantum " richiede che i circuiti vengano trasformati in modo da utilizzare esclusivamente istruzioni supportate dalla QPU (denominati "circuiti ISA"). Le primitive di riferimento continuano ad accettare istruzioni astratte, poiché si basano su simulazioni con vettori di stato locali, ma la transpilazione del circuito potrebbe comunque rivelarsi vantaggiosa in termini di ottimizzazione del circuito stesso.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)

Inizializza SamplerV2

Istanziare qiskit.primitives.StatevectorSampler:

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

Corri e ottieni risultati

# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")

Output:

 > Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

Le primitive accettano più PUB come input e ogni PUB ottiene il proprio risultato. Pertanto, è possibile eseguire diversi circuiti con varie combinazioni di parametri/osservabili e recuperare i risultati PUB :

from qiskit.transpiler import generate_preset_pass_manager

# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()

# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")

Output:

 > Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

Ottieni la distribuzione di probabilità o il risultato della misurazione

I campioni dei risultati della misurazione vengono restituiti come bitstring o conteggi. Le stringhe di bit mostrano i risultati della misurazione, conservando l'ordine di scatto in cui sono stati misurati. Gli oggetti risultato del campionatore organizzano i dati in termini di nomi dei registri classici dei circuiti di ingresso, per compatibilità con i circuiti dinamici.

Il nome del registro classico è predefinito a "meas". Questo nome verrà utilizzato in seguito per accedere alle stringhe di bit delle misure.

# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()

Output:

        ┌───┐      ░ ┌─┐   
   q_0: ┤ H ├──■───░─┤M├───
        └───┘┌─┴─┐ ░ └╥┘┌─┐
   q_1: ─────┤ X ├─░──╫─┤M├
             └───┘ ░  ║ └╥┘
meas: 2/══════════════╩══╩═
                      0  1 
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using Sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")

Output:

The number of bitstrings is: 1024
The counts are: {'00': 538, '11': 486}

Modifica opzioni di esecuzione

Per impostazione predefinita, il campionatore di riferimento esegue un calcolo esatto del vettore di stato in base al valore di quantum_info.Statevector classe. Tuttavia, questo può essere modificato per introdurre l'effetto dell'overhead di campionamento (noto anche come "shot noise"). Per gestire questo overhead, l'interfaccia Sampler accetta un argomento shots che può essere definito al livello PUB.

Questo esempio presuppone che siano stati definiti due circuiti.

# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_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.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])

Output:

<qiskit.primitives.primitive_job.PrimitiveJob at 0x7f94d59db010>

Per un esempio completo, consulta la pagina degli esempi di Sampler.


Passi successivi

Suggerimenti
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.