Skip to main content
IBM Quantum Platform

Simulación exacta con primitivas e Qiskit SDK

  • El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o versiones más recientes.

    qiskit[all]~=2.5.0
    

Las primitivas de referencia del paquete « Qiskit SDK » realizan simulaciones de vectores de estado locales. Estas simulaciones no permiten modelar el ruido de los dispositivos, pero resultan útiles para crear rápidamente prototipos de algoritmos antes de recurrir a técnicas de simulación más avanzadas ( utilizando Qiskit Aer ) o de ejecutarlos en dispositivos reales ( primitivas de Qiskit Runtime ).

La primitiva Estimador puede calcular valores de expectativa de circuitos, y la primitiva Muestreador puede muestrear de las distribuciones de salida de los circuitos.

Las siguientes secciones muestran cómo utilizar las primitivas de referencia para ejecutar su flujo de trabajo localmente.


Utilice el estimador de referencia

La implementación de referencia de EstimatorV2 en qiskit.primitives que se ejecuta en un vector de estado local local es la StatevectorEstimator clase Puede tomar circuitos, observables y parámetros como entradas y devuelve los valores de expectativa calculados localmente.

El siguiente código prepara los datos de entrada que se utilizarán en los ejemplos que vienen a continuación. El tipo de entrada esperado para los observables es qiskit.quantum_info.SparsePauliOp. Ten en cuenta que el circuito del ejemplo está parametrizado, pero también puedes ejecutar Estimator en circuitos no parametrizados.

Note

Cualquier circuito pasado a un Estimador no debe incluir ninguna medida.

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 a circuitos ISA y observables

El flujo de trabajo de primitivas de Qiskit Runtime requiere que los circuitos y observables se transformen para que sólo utilicen instrucciones compatibles con la QPU (denominados circuitos y observables de arquitectura de conjunto de instrucciones (ISA )). Las primitivas de referencia siguen aceptando instrucciones abstractas, ya que se basan en simulaciones de vectores de estado locales, pero transpilar el circuito podría seguir siendo beneficioso en términos de optimización del circuito.

# 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)

Inicializar estimador

Instanciar a qiskit.primitives.StatevectorEstimator.

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

Corre y obtén resultados

Este ejemplo sólo utiliza un circuito (de tipo QuantumCircuit) y un observable.

Ejecute la estimación llamando al método StatevectorEstimator.run que devuelve una instancia de un objeto PrimitiveJob objeto. Puede obtener los resultados del trabajo (como un qiskit.primitives.PrimitiveResult objeto) con el método qiskit.primitives.PrimitiveJob.result método.

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'>

Obtenga el valor esperado del resultado

El resultado de las primitivas genera una matriz de PubResult objetos, donde cada elemento de la matriz es un PubResult objeto que contiene en sus datos la matriz de evaluaciones correspondiente a cada combinación observable del circuito en el espacio de estados ( PUB ).

Para recuperar los valores de las expectativas y los metadatos de la primera (y en este caso, única) evaluación del circuito, debemos acceder a la evaluación data de 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': {}}

Configurar las opciones de ejecución del estimador

Por defecto, el Estimador de referencia realiza un cálculo exacto del vector de estado basado en el quantum_info.Statevector clase. Sin embargo, esto puede modificarse para introducir el efecto de la sobrecarga de muestreo (también conocido como "ruido de disparo").

Estimator acepta un argumento precision que expresa las barras de error que la implementación primitiva debe apuntar para las estimaciones de los valores de expectativa. Se trata de la sobrecarga de muestreo y se define exclusivamente en el método .run() . Esto le permite ajustar la opción hasta el nivel PUB.

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

Para ver un ejemplo completo, consulta la página de ejemplos de Estimator.


Utilice el muestreador de referencia

La implementación de referencia de SamplerV2 en qiskit.primitives es la clase StatevectorSampler clase Toma circuitos y parámetros como entradas y devuelve los resultados del muestreo de las distribuciones de probabilidad de salida como una distribución cuasi-probabilística de los estados de salida.

El siguiente código prepara los datos de entrada que se utilizan en los ejemplos que vienen a continuación. Ten en cuenta que estos ejemplos ejecutan un único circuito parametrizado, pero también puedes ejecutar Sampler en circuitos no parametrizados.

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

Cualquier circuito cuántico pasado a un Muestreador debe incluir mediciones.

Transpile a circuitos ISA y observables

El flujo de trabajo de primitivas de Qiskit Runtime requiere que los circuitos se transformen para que sólo utilicen instrucciones soportadas por la QPU (denominados circuitos ISA). Las primitivas de referencia siguen aceptando instrucciones abstractas, ya que se basan en simulaciones de vectores de estado locales, pero transpilar el circuito podría seguir siendo beneficioso en términos de optimización del circuito.

# 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)

Inicializar SamplerV2

Instanciar qiskit.primitives.StatevectorSampler:

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

Corre y obtén resultados

# 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'>

Las primitivas aceptan múltiples PUBs como entradas, y cada PUB obtiene su propio resultado. Por lo tanto, puede ejecutar diferentes circuitos con varias combinaciones de parámetros/observables y recuperar los resultados de 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'>

Obtenga la distribución de probabilidad o el resultado de la medición

Las muestras de resultados de medición se devuelven como cadenas de bits o recuentos. Las cadenas de bits muestran los resultados de las mediciones, conservando el orden de disparo en el que se midieron. Los objetos de resultados del Muestreador organizan los datos en términos de los nombres de registro clásicos de sus circuitos de entrada, por compatibilidad con los circuitos dinámicos.

El nombre del registro clásico es, por defecto, "meas". Este nombre se utilizará más adelante para acceder a las cadenas de bits de medición.

# 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: {'11': 519, '00': 505}

Cambiar opciones de ejecución

Por defecto, el Muestreador de referencia realiza un cálculo exacto del vector de estado basado en el quantum_info.Statevector clase. Sin embargo, esto puede modificarse para introducir el efecto de la sobrecarga de muestreo (también conocido como "ruido de disparo"). Para ayudar a gestionar esta sobrecarga, la interfaz Sampler acepta un argumento shots que puede definirse en el nivel PUB.

Este ejemplo supone que ha definido dos circuitos.

# 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 0x7fab09442490>

Para ver un ejemplo completo, consulta la página de ejemplos de Sampler.


Próximos pasos

Recomendaciones
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.