Skip to main content
IBM Quantum Platform

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 en circuitos parametrizados) y de tomar muestras de sus registros de salida clásicos. Los estimadores admiten combinaciones de circuitos y observables (o barridos de los mismos) para estimar los valores esperados de los observables.

Qiskit ofrece una implementación de referencia para cada una de estas abstracciones en las StatevectorSampler clases y StatevectorEstimator .

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 run método y, en la práctica, han sido sustituidas en gran medida por BaseSamplerV2 y BaseEstimatorV2. No obstante, se han conservado las definiciones originales de las interfaces abstractas para garantizar la compatibilidad con versiones anteriores. Consulta la sección sobre migración de esta página para obtener más detalles sobre la diferencia entre V1 y V2.


Descripción general de EstimatorV2

BaseEstimatorV2 es una función primitiva que calcula los valores esperados para las combinaciones de circuitos cuánticos y observables proporcionadas.

Tras la construcción, se utiliza un estimador llamando a su run() método con una lista de pubs (bloques primitivos unificados). Cada pub contiene tres valores que, en conjunto, definen una unidad de trabajo de cálculo que debe completar el estimador:

  • una única variable QuantumCircuit, posiblemente parametrizada, cuyo estado final definimos como ψ(θ)\psi(\theta),
  • una o más variables observables (especificadas como cualquier ObservablesArrayLike, incluyendo Pauli, SparsePauliOp, str) que determinan qué valores esperados se van a estimar, denotadas c HjH_j y
  • una colección de conjuntos de valores de parámetros a los que vincular el circuito, θk\theta_k.

Al ejecutar un estimador se obtiene un BasePrimitiveJob objeto, en el que al llamar al método result() se obtienen estimaciones de los valores esperados y metadatos para cada publicación:

ψ(θk)Hjψ(θk)\langle\psi(\theta_k)|H_j|\psi(\theta_k)\rangle

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 toma muestras de las salidas de los circuitos cuánticos.

Una vez finalizada la construcción, se utiliza un muestreador llamando a su run() método con una lista de pubs (Primitive Unified Blocs). Cada pub contiene valores que, en conjunto, definen una unidad computacional de trabajo que debe completar el muestreador:

  • 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 ninguna implementación de la interfaz heredada EstimatorV1 en Qiskit. Sin embargo, la definición de la interfaz abstracta de BaseEstimatorV1 sigue formando parte del paquete para garantizar la compatibilidad con versiones anteriores de las implementaciones externas.

Una EstimatorV1 implementación se inicializa con un conjunto de parámetros vacío. BaseEstimatorV1 Se puede invocar mediante el .run() método con los siguientes parámetros:

  • circuitos cuánticos ( ψi(θ)\psi_i(\theta) ): lista de circuitos cuánticos (parametrizados) (una lista de QuantumCircuit objetos).
  • observables ( HjH_j ): una lista de SparsePauliOp objetos.
  • valores de los parámetros ( θk\theta_k ): 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 qiskit.providers.JobV1.result() a esta función, se obtiene una lista de valores esperados, además de metadatos opcionales, como los intervalos de confianza de la estimación.

ψi(θk)Hjψi(θk)\langle\psi_i(\theta_k)|H_j|\psi_i(\theta_k)\rangle

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 ninguna implementación de la interfaz heredada SamplerV1 en Qiskit. Sin embargo, la definición de la interfaz abstracta de BaseSamplerV1 sigue formando parte del paquete para garantizar la compatibilidad con versiones anteriores de las implementaciones externas.

Las clases de muestreador calculan probabilidades o cuasiprobabilidades de cadenas de bits a partir de circuitos cuánticos.

A SamplerV1 se inicializa con un conjunto de parámetros vacío. BaseSamplerV1 Las implementaciones se pueden invocar mediante el .run() método con los siguientes parámetros:

  • circuitos cuánticos ( ψi(θ)\psi_i(\theta) ): lista de circuitos cuánticos (parametrizados). (una lista de QuantumCircuit objetos)
  • valores de parámetros ( θk\theta_k ): 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. Al llamar a esta función qiskit.providers.JobV1.result() se obtiene un SamplerResult objeto 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:

  1. 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().values

    En 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
  2. 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 equivalente más cercano a las distribuciones de cuasiprobabilidad en la interfaz « V2 » es el get_counts() método que se muestra en el ejemplo siguiente. No obstante, queremos destacar que, en el caso de los experimentos a escala industrial (más de 100 qubits), las probabilidades de medir la misma cadena de bits dos veces son escasas, por lo que agrupar los recuentos en formato de diccionario no suele ser una estrategia eficaz para el 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()})
  3. Las primitivas de « 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 shots argumento ahora forma parte de la run() firma y, además, que cada pub puede especificar su propio valor para shots, el cual tiene prioridad sobre cualquier valor asignado al método. El estimador tiene un argumento análogo precision que especifica las barras de error a las que debe aspirar la implementación primitiva para 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

Columna « 1 »
Columna « 2 »
ParameterLikeRepresentar un tipo de unión
BindingsArray( [datos, forma] )Almacena conjuntos de valores de enlace de parámetros para un qiskit.QuantumCircuit.
Columna « 1 »
Columna « 2 »
BindingsArrayLikealias de `Mapping[ParameterLike

Estimador V2

Columna « 1 »
Columna « 2 »
BaseEstimatorV2()Clase base para implementaciones de EstimatorV2 .
StatevectorEstimator(*[, precisión_por_defecto,...] )Implementación sencilla de BaseEstimatorV2 con simulación completa del vector 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.
ObservableLikeRepresentar un tipo de unión
Columna « 1 »
Columna « 2 »
EstimatorPubLikealias de EstimatorPub
ObservablesArrayLikealias de `ObservableLike

Muestreador V2

Columna « 1 »
Columna « 2 »
BaseSamplerV2()Clase base para implementaciones de SamplerV2 .
StatevectorSampler(*[, default_shots, seed] )Implementación sencilla mediante BaseSamplerV2 simulación con vector de estado completo.
BackendSamplerV2(*, backend[, opciones] )Evalúa las cadenas de bits de los circuitos cuánticos proporcionados
SamplerPub(circuito[, valores_de_parámetros,...] )Pub (Bloque Unificado Primitivo) para un sampler.
Columna « 1 »
Columna « 2 »
SamplerPubLikealias de SamplerPub

Resultados V2

Columna « 1 »
Columna « 2 »
BitArray(matriz, num_bits)Almacena una matriz de valores de bits.
DataBin(*[, forma] )PubResultLos datos principales proceden de un único pub de entre...
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(función, *args, **kwargs)Maneje un trabajo a partir de las implementaciones de referencia de los primitivos en Qiskit.

Estimador V1

Columna « 1 »
Columna « 2 »
BaseEstimatorV1(*[, opciones] )Clase base para implementaciones de EstimatorV1 .
EstimatorResult(valores, metadatos)Resultado del estimador V1.

Muestreador V1

Columna « 1 »
Columna « 2 »
BaseSamplerV1(*[, opciones] )Clase base Sampler V1
SamplerResult(quasi_dists, metadatos)Resultado del muestreador V1.
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.