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.
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:
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]]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:
Cualquier circuito cuántico pasado a un Muestreador debe incluir mediciones.
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
- Para una simulación de mayor rendimiento que pueda manejar circuitos más grandes, o para incorporar modelos de ruido a su simulación, consulte Simulación exacta y ruidosa con primitivas Qiskit Aer.
- Para aprender a utilizar Quantum Composer para simulación, consulte la guía de IBM Quantum Composer.
- Lea la referencia de la API de Qiskit Estimator.
- Lea la referencia de la API del muestreador Qiskit.
- Lea Migrar a V2 primitives.