Descripción general

El readout es el mecanismo que controla qué información se extrae del backend cuántico después de que se ejecuta un funcional. Se especifica de forma independiente del propio funcional, de modo que el mismo circuito o secuencia puede medirse de diferentes maneras sin ningún cambio de código en el funcional.

Todos los usos comienzan con una instancia de la clase Readout.

Readout es un constructor que acumula métodos de readout encadenando llamadas with_*. Cada llamada devuelve una nueva especificación con un slot adicional rellenado; el objeto original nunca se modifica.

from qilisdk.readout import Readout
from qilisdk.analog import Z

spec = Readout()                                  # no readout selected yet
spec = spec.with_sampling(nshots=1000)            # add sampling
spec = spec.with_expectation(observables=[Z(0)])  # add expectation values

# or in one line:
spec = Readout().with_sampling(nshots=1000).with_expectation(observables=[Z(0)])

La especificación finalizada se pasa a execute() (o submit() para hardware remoto):

from qilisdk.backends import QiliSim
from qilisdk.digital import Circuit
from qilisdk.functionals import DigitalPropagation

backend = QiliSim()
circuit = Circuit(2)
functional = DigitalPropagation(circuit)
result = backend.execute(functional, readout=spec)

Nota

Se debe añadir al menos un método de readout antes de llamar a execute. Pasar un Readout() vacío genera un ValueError.

Cualquier combinación de los tres tipos de readout puede solicitarse en una sola ejecución encadenando las llamadas with_*. El backend realiza una ejecución del circuito y extrae todas las salidas solicitadas.

from qilisdk.analog import Z
from qilisdk.readout import Readout

spec = (
    Readout()
    .with_sampling(nshots=500)
    .with_expectation(observables=[Z(0)])
    .with_state_tomography()
)
result = backend.execute(functional, readout=spec)

counts = result.get_samples()              # from sampling
ev     = result.get_expectation_values()   # from expectation
state  = result.get_state()                # from state tomography

Acceso a Resultados

execute() devuelve un FunctionalResult. Hay dos formas de acceder a los datos de readout.

Atajos Convenientes

La forma más rápida de acceder a los resultados:

Propiedad

Tipo

Requiere

get_samples()

dict[str, int]

with_sampling()

get_probabilities()

dict[str, float]

with_sampling() o with_state_tomography()

get_expectation_values()

list[float]

with_expectation()

get_state()

QTensor

with_state_tomography()

Estos generan ValueError en tiempo de ejecución si el readout correspondiente no fue solicitado.

Propiedades de Reenvío con Tipos

Para código de producción o cuando se combinan múltiples tipos de readout, use las propiedades de reenvío con tipos. Estas devuelven los objetos de resultado sin procesar y permiten que el verificador de tipos valide el acceso sin guardas en tiempo de ejecución:

from qilisdk.analog import Z
from qilisdk.readout import Readout

spec = Readout().with_sampling(nshots=1000).with_expectation(observables=[Z(0)])
result = backend.execute(functional, readout=spec)

# result.sampling is SamplingReadoutResult - the type checker knows this
top2 = result.sampling.get_probabilities(n=2)

# result.expectation is ExpectationReadoutResult - the type checker knows this
raw_evs = result.expectation.expectation_values

Propiedad

Devuelve

Requiere

result.sampling

SamplingReadoutResult | None

with_sampling()

result.expectation

ExpectationReadoutResult | None

with_expectation()

result.state_tomography

StateTomographyReadoutResult | None

with_state_tomography()

Ejemplo Completo

El siguiente ejemplo ejecuta una secuencia de recocido analógico, recopila los valores esperados y el estado final en cada paso temporal, y luego representa la trayectoria del observable.

import numpy as np
import matplotlib.pyplot as plt

from qilisdk.analog import Schedule, X, Z
from qilisdk.backends import QutipBackend
from qilisdk.core import ket
from qilisdk.functionals import AnalogEvolution
from qilisdk.readout import Readout

T = 5.0
schedule = Schedule(
    hamiltonians={"driver": X(0), "problem": Z(0)},
    coefficients={
        "driver": {(0.0, T): lambda t: 1.0 - t / T},
        "problem": {(0.0, T): lambda t: t / T},
    },
    dt=0.1,
)

functional = AnalogEvolution(
    schedule=schedule,
    initial_state=(ket(0) - ket(1)).unit(),
    store_intermediate_results=True,
)

spec = (
    Readout()
    .with_expectation(observables=[Z(0)])
    .with_state_tomography()
)

result = QutipBackend().execute(functional, readout=spec)

# Final results
print("Final <Z>:", result.get_expectation_values()[0])
print("Final state:", result.get_state())

# Time-resolved expectation values
ev_trajectory = [step[0] for step in result.get_intermediate_expectation_values()]
plt.plot(np.linspace(0, T, len(ev_trajectory)), ev_trajectory)
plt.xlabel("Time")
plt.ylabel("⟨Z⟩")
plt.title("Expectation value during annealing")
plt.show()