Primitivos
qiskit.primitives
Las primitivas son bloques de construcción computacionales que se utilizarán en aplicaciones de mayor envergadura cuyas unidades de entrada, denominadas bloques unificados primitivos (PUB), requieren recursos cuánticos para producir salidas de forma eficiente.
Actualmente existen dos tipos de primitivas cuyas abstracciones, en sus últimas versiones, están definidas por BaseSamplerV2 y BaseEstimatorV2. Los muestreadores se encargan de aceptar circuitos cuánticos (o barridos de valores sobre circuitos parametrizados) y tomar muestras de sus registros de salida clásicos. Los estimadores aceptan combinaciones de circuitos y observables (o barridos de los mismos) para estimar los valores de las expectativas de los observables.
Qiskit ofrece una implementación de referencia para cada una de estas abstracciones en los módulos StatevectorSampler y StatevectorEstimator clases.
Las versiones anteriores de las abstracciones del muestreador y del estimador se definen mediante BaseSamplerV1 y BaseEstimatorV1. Estas interfaces siguen un formato de entrada-salida diferente y menos flexible para el método run y han sido sustituidas en gran medida en la práctica por BaseSamplerV2 y BaseEstimatorV2. No obstante, se han conservado las definiciones abstractas originales de las interfaces por compatibilidad con versiones anteriores. Consulte la sección de migración de esta página para ver más detalles sobre la diferencia entre V1 y V2.
Descripción general de EstimatorV2
BaseEstimatorV2 es una primitiva que estima valores de expectativa para combinaciones de circuito cuántico y observable proporcionadas.
Tras la construcción, se utiliza un estimador llamando a su método run() con una lista de pubs (bloques unificados primitivos). Cada pub contiene tres valores que, juntos, definen una unidad de trabajo de cálculo que debe completar el estimador:
- una única variable
QuantumCircuit, posiblemente parametrizada, cuyo estado final definimos como , - uno o más observables (especificados como cualquier
ObservablesArrayLike, incluyendoPauli,SparsePauliOpstr) que especifican qué valores de expectativa estimar, denotados , y - una colección de conjuntos de valores de parámetros a los que vincular el circuito, .
La ejecución de un estimador devuelve un objeto BasePrimitiveJob donde al llamar al método result() se obtienen las estimaciones de los valores de las expectativas y los metadatos de cada pub:
Los observables y la parte de los valores de los parámetros de un pub pueden estar valorados por matrices con dimensiones arbitrarias, en las que se aplican reglas de emisión estándar, de modo que, a su vez, el resultado estimado para cada pub también está, en general, valorado por matrices. Para más información, consulte aquí.
He aquí un ejemplo de cómo se utiliza un estimador.
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}")Descripción general de SamplerV2
BaseSamplerV2 es una primitiva que muestrea las salidas de los circuitos cuánticos.
Tras su construcción, un muestreador se utiliza llamando a su método run() con una lista de pubs (bloques unificados primitivos). Cada pub contiene valores que, juntos, definen una unidad de trabajo computacional que el muestreador debe completar:
- Un único
QuantumCircuitposiblemente parametrizado. - Una colección de conjuntos de valores de parámetros para enlazar el circuito si es paramétrico.
- Opcionalmente, el número de disparos a muestrear, determinado en el método de ejecución si no se ha establecido.
Al ejecutar un sampler se obtiene un BasePrimitiveJob objeto, en el que al llamar al método result() se obtienen muestras de salida y metadatos para cada publicación.
He aquí un ejemplo de cómo se utiliza un muestreador.
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}")Descripción general de EstimatorV1
Actualmente no hay implementaciones de la interfaz EstimatorV1 en Qiskit. Sin embargo, la definición de interfaz abstracta de BaseEstimatorV1 sigue formando parte del paquete para ofrecer compatibilidad con implementaciones externas.
Una implementación de EstimatorV1 se inicializa con un conjunto de parámetros vacío. BaseEstimatorV1 se puede llamar a través del método .run() con los siguientes parámetros:
- circuitos cuánticos ( ): lista de circuitos cuánticos (parametrizados) (una lista de
QuantumCircuitobjetos). - observables ( ): una lista de
SparsePauliOpobjetos. - valores de los parámetros ( ): lista de conjuntos de valores que deben vincularse a los parámetros de los circuitos cuánticos (lista de lista de float).
El método debe devolver un JobV1 objeto. Al llamar a esta función qiskit.providers.JobV1.result() se obtiene una lista de valores esperados, además de metadatos opcionales, como los intervalos de confianza de la estimación.
He aquí un ejemplo de cómo se utilizaría una aplicación de EstimatorV1 . Tenga en cuenta que actualmente no hay implementaciones de la interfaz EstimatorV1 heredada en 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}")Descripción general de SamplerV1
Actualmente no hay implementaciones de la interfaz SamplerV1 en Qiskit. Sin embargo, la definición de interfaz abstracta de BaseSamplerV1 sigue formando parte del paquete para ofrecer compatibilidad con implementaciones externas.
Las clases de muestreador calculan probabilidades o cuasiprobabilidades de cadenas de bits a partir de circuitos cuánticos.
Un SamplerV1 se inicializa con un conjunto de parámetros vacío. BaseSamplerV1 a través del método .run() con los siguientes parámetros:
- circuitos cuánticos ( ): lista de circuitos cuánticos (parametrizados). (una lista de
QuantumCircuitobjetos) - valores de parámetros ( ): lista de conjuntos de valores de parámetros que deben vincularse a los parámetros de los circuitos cuánticos. (lista de lista de flotador)
.run() devolverá un JobV1 objeto. Si se llama a qiskit.providers.JobV1.result() produce un objeto SamplerResult que contiene probabilidades o cuasiprobabilidades de cadenas de bits, además de metadatos opcionales como barras de error en las muestras.
He aquí un ejemplo de cómo se utilizaría una aplicación de SamplerV1 . Tenga en cuenta que actualmente no hay implementaciones de la interfaz SamplerV1 heredada en 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])Migración de Primitives V1 a V2
La diferencia formal entre las API de tipos primitivos V1 y V2 radica en las clases base de las que heredan las implementaciones de los tipos primitivos, todas ellas enumeradas al final de la página. Sin embargo, desde un punto de vista conceptual, hay algunas diferencias importantes que hay que tener en cuenta al migrar de V1 a V2:
-
Las primitivas V2 favorecen las entradas vectorizadas, en las que los circuitos individuales pueden agruparse con especificaciones de valor vectorial (o, más generalmente, de valor de matriz). Cada grupo se denomina bloque unificado primitivo (pub), y cada pub obtiene su propio resultado. Por ejemplo, en el estimador, puede comparar las siguientes diferencias:
# 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().valuesEn el ejemplo anterior no se muestra, por brevedad, que el circuito puede ser paramétrico, con matrices de conjuntos de valores de parámetros emitidos contra la matriz de observables. El muestreador es similar, pero sin observables:
# 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 -
El muestreador V2 devuelve muestras de resultados clásicos, conservando el orden de disparo en el que se midieron. Esto contrasta con el muestreador V1 que produce distribuciones cuasi-probables que son, en cambio, una estimación de la distribución sobre los resultados clásicos. Además, los objetos de resultados del muestreador V2 organizan los datos en términos de los nombres de registro clásicos de sus circuitos de entrada, lo que proporciona una compatibilidad natural con los circuitos dinámicos.
El análogo más cercano de las distribuciones de cuasiprobabilidad en la interfaz V2 es el método
get_counts()que se muestra en el siguiente ejemplo. Sin embargo, hay que destacar que en los experimentos a gran escala (más de 100 qubits), las posibilidades de medir la misma cadena de bits dos veces son escasas, por lo que la agrupación de recuentos en un formato de diccionario no suele ser una estrategia eficaz de procesamiento de datos.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()}) -
Las primitivas de la biblioteca « V2 » han trasladado el concepto de sobrecarga de muestreo —inherente a todos los sistemas cuánticos debido a su naturaleza probabilística— de las opciones a la propia API. Para el sampler, esto significa que el
shotsargumento ahora forma parte de larun()firma y, además, que cada pub puede especificar su propio valor parashots, el cual tiene prioridad sobre cualquier valor asignado al método. El estimador tiene un argumentoprecisionanálogo que especifica los intervalos de confianza que la implementación primitiva debe intentar alcanzar en las estimaciones del valor esperado.Este concepto no está presente en la API de las primitivas V1, aunque todas las implementaciones de las primitivas V1 tienen ajustes relacionados en algún lugar de sus opciones.
# 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 de primitivas
Parámetros V2
ParameterLike | Representar un tipo de unión |
BindingsArray( [datos, forma] ) | Almacena conjuntos de valores de enlace de parámetros para un qiskit.QuantumCircuit. |
BindingsArrayLike | alias de `Mapping[ParameterLike |
Estimador V2
BaseEstimatorV2() | Clase base para implementaciones de EstimatorV2 . |
StatevectorEstimator(*[, default\precision,...] ) | Implementación sencilla de BaseEstimatorV2 con simulación completa de vectores de estado. |
BackendEstimatorV2(*, backend[, opciones] ) | Evalúa valores de expectativa para combinaciones de circuito cuántico y observable proporcionadas. |
EstimatorPub(circuito, magnitudes observables[,...] ) | Bloque unificado primitivo para cualquier primitiva del estimador. |
ObservablesArray(observables[, num_qubits,...] ) | Un conjunto de dimensión n de observables hermitianos para un Estimator primitivo. |
ObservableLike | Representar un tipo de unión |
EstimatorPubLike | alias de EstimatorPub |
ObservablesArrayLike | alias de `ObservableLike |
Muestreador V2
BaseSamplerV2() | Clase base para implementaciones de SamplerV2 . |
StatevectorSampler(*[, default_shots, seed] ) | Implementación sencilla de BaseSamplerV2 mediante simulación vectorial completa de estados. |
BackendSamplerV2(*, backend[, opciones] ) | Evalúa las cadenas de bits de los circuitos cuánticos proporcionados |
SamplerPub(circuito[, valores de los parámetros,...] ) | Pub (Bloque Unificado Primitivo) para un sampler. |
SamplerPubLike | alias de SamplerPub |
Resultados V2
BitArray(array, num_bits) | Almacena una matriz de valores de bits. |
DataBin(*[, forma] ) | Los principales datos proceden de un solo pub de PubResult. |
PrimitiveResult(pub_results[, metadata] ) | Un contenedor para múltiples resultados de pub y metadatos globales. |
PubResult(datos[, metadatos] ) | El objeto resultado para un solo pub (bloque unificado primitivo). |
SamplerPubResult(datos[, metadatos] ) | Resultado de Sampler Pub. |
BasePrimitiveJob(job_id, **kwargs) | Clase base abstracta de trabajo primitivo. |
PrimitiveJob(function, *args, **kwargs) | Maneje un trabajo a partir de las implementaciones de referencia de los primitivos en Qiskit. |
Estimador V1
BaseEstimatorV1(*[, opciones] ) | Clase base para implementaciones de EstimatorV1 . |
EstimatorResult(valores, metadatos) | Resultado del estimador V1. |
Muestreador V1
BaseSamplerV1(*[, opciones] ) | Clase base Sampler V1 |
SamplerResult(cuasi_distas, metadatos) | Resultado del muestreador V1. |