Visió General

El readout és el mecanisme que controla quina informació s’extreu del backend quàntic després que s’executi un funcional. S’especifica separadament del propi funcional, de manera que el mateix circuit o schedule pot ser mesurat de maneres diferents sense cap canvi de codi en el funcional.

Tots els usos comencen amb una instància de la classe Readout.

Readout és un constructor que acumula mètodes de readout encadenant crides with_*. Cada crida retorna una nova especificació amb un slot addicional omplert; l’objecte original mai es 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)])

L’especificació acabada es passa a execute() (o submit() per al maquinari remot):

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

Cal afegir almenys un mètode de readout abans de cridar execute. Passar un Readout() buit llença un ValueError.

Es pot sol·licitar qualsevol combinació dels tres tipus de readout en una sola execució encadenant les crides with_*. El backend realitza una execució del circuit i extreu totes les sortides sol·licitades.

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

Accés als Resultats

execute() retorna un FunctionalResult. Hi ha dues maneres d’accedir a les dades de readout.

Dreceres Convenients

La manera més ràpida d’accedir als resultats:

Propietat

Tipus

Requereix

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

Aquests llencen ValueError en temps d’execució si el readout corresponent no s’havia sol·licitat.

Propietats de Reenviament Tipificat

Per al codi de producció o quan es combinen múltiples tipus de readout, utilitzeu les propietats de reenviament tipificat. Aquestes retornen els objectes de resultat en brut i permeten que el verificador de tipus comprovi l’accés sense guàrdies en temps d’execució:

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

Propietat

Retorna

Requereix

result.sampling

SamplingReadoutResult | None

with_sampling()

result.expectation

ExpectationReadoutResult | None

with_expectation()

result.state_tomography

StateTomographyReadoutResult | None

with_state_tomography()

Exemple Complet

L’exemple següent executa un schedule d’annealing analògic, recull valors esperats i l’estat final a cada pas de temps, i després representa la trajectòria de l’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()