SpeQtrum
El paquete speqtrum proporciona un cliente sincrónico opcional para la nube SpeQtrum de Qilimanjaro. A través de la clase SpeQtrum se puede autenticar, inspeccionar dispositivos y trabajos, y enviar experimentos digitales, analógicos o de pulso para su ejecución remota.
Instalación
El soporte de SpeQtrum se distribuye como un grupo de dependencias opcional. Instálelo junto con QiliSDK con:
pip install qilisdk[speqtrum]
pip install "qilisdk[speqtrum]"
pip install qilisdk[speqtrum]
Autenticación
La API utiliza tokens OAuth de corta duración que se almacenan en caché en el llavero del sistema. Llame a SpeQtrum.login una vez y las credenciales se reutilizarán en sesiones posteriores.
from qilisdk.speqtrum import SpeQtrum
# Credentials can be provided explicitly…
logged_in = SpeQtrum.login(username="alice", apikey="MY_SECRET_KEY")
# …or read from the environment (QILISDK_SPEQTRUM_USERNAME / QILISDK_SPEQTRUM_APIKEY)
logged_in = SpeQtrum.login()
if not logged_in:
raise RuntimeError("Authentication failed")
# Remove cached credentials when they are no longer needed
SpeQtrum.logout()
Construcción del cliente
Una vez almacenadas las credenciales, instancie SpeQtrum para comenzar a emitir solicitudes. La construcción falla con un RuntimeError si no existen credenciales en caché.
from qilisdk.speqtrum import SpeQtrum
client = SpeQtrum()
Catálogo de dispositivos
Los dispositivos se representan mediante modelos Device que contienen el código del dispositivo, el número de cúbits, el tipo de hardware y el estado. Use SpeQtrum.list_devices para enumerarlos. Un predicado where opcional permite el filtrado en el lado del cliente.
from qilisdk.speqtrum import SpeQtrum, DeviceStatus
client = SpeQtrum()
for device in client.list_devices(where=lambda d: d.status == DeviceStatus.ONLINE):
print(f"{device.code}: {device.name} ({device.type}) – {device.nqubits} qubits")
Trabajos remotos
SpeQtrum.list_jobs devuelve registros ligeros JobInfo. El predicado where funciona de la misma manera que con los dispositivos.
from qilisdk.speqtrum import SpeQtrum
from qilisdk.speqtrum.speqtrum_models import JobStatus
client = SpeQtrum()
running = client.list_jobs(where=lambda job: job.status == JobStatus.RUNNING)
for job in running:
print(f"{job.id}: {job.status.value} on {job.device_id}")
Para inspeccionar los metadatos completos de un trabajo (carga útil, resultado, registros, errores decodificados) llame a SpeQtrum.get_job. Los campos binarios se devuelven como cadenas decodificadas u objetos ExecuteResult estructurados.
Cuando se espera a un JobHandle, el objeto devuelto es un TypedJobDetail que expone un auxiliar con tipado fuerte get_results().
job_handle = client.submit(sampling, device=device)
final_job = client.wait_for_job(job_handle)
result = final_job.get_results() # -> FunctionalResult
Aún puede llamar a get_job() con un identificador entero simple. En ese caso se devuelve un objeto regular JobDetail y puede inspeccionar manualmente los campos *.result individuales cuando sea necesario.
Espera de finalización
Use SpeQtrum.wait_for_job para sondear hasta que un trabajo alcance un estado terminal (completed, error o cancelled). Pasar un JobHandle produce un TypedJobDetail con acceso tipado a los resultados, mientras que los identificadores enteros simples continúan devolviendo un JobDetail simple. El auxiliar lanza un TimeoutError si el tiempo de espera opcional expira primero.
Envío de funcionales
SpeQtrum acepta los mismos funcionales primitivos utilizados por los backends locales. El método SpeQtrum.submit inspecciona el tipo de funcional y serializa la carga útil correcta. Debe proporcionar un argumento device con el código de dispositivo obtenido de list_devices().
from qilisdk.digital import Circuit, H, CNOT
from qilisdk.functionals import DigitalPropagation
from qilisdk.readout import Readout
from qilisdk.speqtrum import SpeQtrum
circuit = Circuit(2)
circuit.add(H(0))
circuit.add(CNOT(0, 1))
functional = DigitalPropagation(circuit)
client = SpeQtrum()
device = client.list_devices()[0].code
job_handle = client.submit(functional, readout=Readout().with_sampling(nshots=1_000), device=device)
print("Submitted job:", job_handle.id)
final_job = client.wait_for_job(job_handle, timeout=600)
result = final_job.get_results()
print("Most frequent outcome:", result.probabilities)
Advertencia
Las QPU físicas actualmente no soportan funcionales analógicos basados en AnalogEvolution; por ahora, el hardware analógico solo puede ejecutar experimentos de pulso de experiments.
Programas variacionales
La optimización híbrida se gestiona mediante el mismo funcional VariationalProgram utilizado con los backends locales. Serialice el programa variacional completamente configurado (ansatz, optimizador, función de coste) y envíelo como cualquier otro funcional.
from qilisdk.core.model import Model, ObjectiveSense
from qilisdk.core.comparison import LEQ
from qilisdk.core.variables import BinaryVariable
from qilisdk.cost_functions import ModelCostFunction
from qilisdk.digital import CNOT, HardwareEfficientAnsatz, U2
from qilisdk.functionals import DigitalPropagation
from qilisdk.functionals.variational_program import VariationalProgram
from qilisdk.readout import Readout
from qilisdk.optimizers.scipy_optimizer import SciPyOptimizer
from qilisdk.speqtrum import SpeQtrum
# Build a small cost model
vars = [BinaryVariable(f"x{i}") for i in range(3)]
model = Model("toy")
model.set_objective(sum(vars), sense=ObjectiveSense.MAXIMIZE)
model.add_constraint("budget", LEQ(vars[0] + vars[1], 1))
ansatz = HardwareEfficientAnsatz(
nqubits=3,
layers=2,
one_qubit_gate=U2,
two_qubit_gate=CNOT,
connectivity="linear",
structure="grouped",
)
functional = DigitalPropagation(ansatz)
optimizer = SciPyOptimizer(method="Powell")
vprog = VariationalProgram(functional=functional, optimizer=optimizer, cost_function=ModelCostFunction(model))
client = SpeQtrum()
device = client.list_devices()[0].code
job_handle = client.submit(vprog, readout=Readout().with_sampling(nshots=1024), device=device)
Experimentos de pulso
El cliente SpeQtrum también admite experimentos de estilo de calibración. Un experimento es cualquier subclase de ExperimentFunctional; los experimentos concretos (Rabi, T1, T2, espectroscopía, …) se proporcionan de forma privada al usuario de SpeQtrum, junto con la subclase de ExperimentResult correspondiente. Los experimentos se envían sin readout, ya que la adquisición la define el propio experimento.
from qilisdk.speqtrum import DeviceType, SpeQtrum
client = SpeQtrum()
device = client.list_devices(
where=lambda d: d.type in (DeviceType.QPU_ANALOG, DeviceType.QPU_DIGITAL)
)[0].code
# `experiment` is an ExperimentFunctional subclass provided to the user.
handle = client.submit(experiment, device=device)
response = client.wait_for_job(handle, timeout=600)
result = response.get_results()
El ExperimentResult resultante contiene los datos de barrido adquiridos (s21, s21_modulus, s21_db, s21_phase) y su método plot representa directamente el barrido 1D o 2D.