Reservorios Cuánticos
El funcional QuantumReservoir ejecuta un pipeline de reservoir sobre una secuencia de entradas. Cada capa aplica un circuito de preprocesamiento opcional, un bloque de dinámica de reservoir analog (Schedule), y un circuito de postprocesamiento opcional, seguido de mediciones de uno o más observables. La lista opcional qubits_to_reset puede utilizarse para restablecer qubits seleccionados entre capas.
Las entradas del reservoir se representan mediante parámetros ReservoirInput. Estos se comportan como objetos Parameter estándar, pero están marcados como no entrenables para que puedan ser dirigidos por secuencias de datos de entrada en lugar de bucles de optimización.
Parámetros
initial_state (
QTensor): Estado inicial del reservoir.reservoir_layer (
ReservoirLayer): Define el preprocesamiento/postprocesamiento, la dinámica del reservoir, los observables y la política de restablecimiento.input_per_layer (List[Dict[str, float]]): Valores de entrada a aplicar en cada capa, indexados por nombres de parámetros de entrada (normalmente las etiquetas de
ReservoirInput).
Retorna
FunctionalResult: Accede a los valores esperados por capa medianteget_expectation_values(), más estados opcionales medianteget_intermediate_states()si se solicitó tomografía de estado en la especificación de lectura.
Ejemplo de Uso
import numpy as np
from qilisdk.backends import CudaBackend
from qilisdk.core import ket
from qilisdk.digital import Circuit, U2
from qilisdk.functionals.quantum_reservoirs import QuantumReservoir, ReservoirInput, ReservoirLayer
from qilisdk.analog import Schedule, X, Z
from qilisdk.readout import Readout
pre_processing = Circuit(2)
pre_processing.add(U2(1, phi=ReservoirInput("phi_1", 0.1), gamma=ReservoirInput("gamma_1", 0.1)))
res_layer = ReservoirLayer(
evolution_dynamics=Schedule(
hamiltonians={"h": Z(0) + Z(1) + Z(0) * Z(1) + 0.5 * (X(0) + X(1))},
total_time=1.0,
dt=0.1,
),
input_encoding=pre_processing,
qubits_to_reset=[1],
)
reservoir = QuantumReservoir(
initial_state=(np.random.rand() * ket(0, 0) + np.random.rand() * ket(1, 1)).unit(),
reservoir_layer=res_layer,
input_per_layer=[
{"phi_1": 0.2, "gamma_1": 0.1},
{"phi_1": 0.3, "gamma_1": 0.2},
{"phi_1": 0.4, "gamma_1": 0.3},
],
)
results = CudaBackend().execute(
reservoir,
Readout().with_expectation(observables=[Z(0), Z(1), Z(0) * Z(1)]),
)
print(results.get_expectation_values())
Codificación de Datos de Entrada
Se pueden inyectar datos clásicos en una capa del reservoir en múltiples lugares. El único requisito es que las claves en input_per_layer coincidan con las etiquetas de los objetos ReservoirInput que se colocan en la capa.
1. Codificar con circuitos de entrada y salida
from qilisdk.analog import Schedule, X, Z
from qilisdk.core import ket
from qilisdk.digital import Circuit, RX, RY
from qilisdk.functionals.quantum_reservoirs import QuantumReservoir, ReservoirInput, ReservoirLayer
theta_in = ReservoirInput("theta_in", 0.0)
phi_out = ReservoirInput("phi_out", 0.0)
input_circuit = Circuit(2)
input_circuit.add(RX(0, theta=theta_in))
output_circuit = Circuit(2)
output_circuit.add(RY(1, theta=phi_out))
layer = ReservoirLayer(
evolution_dynamics=Schedule(
hamiltonians={"h": Z(0) * Z(1) + 0.3 * (X(0) + X(1))},
total_time=1.0,
dt=0.1,
),
input_encoding=input_circuit,
output_encoding=output_circuit,
)
reservoir = QuantumReservoir(
initial_state=ket(0, 0),
reservoir_layer=layer,
input_per_layer=[
{"theta_in": 0.1, "phi_out": 0.0},
{"theta_in": 0.5, "phi_out": 0.4},
],
)
2. Codificar directamente en los parámetros del Hamiltoniano
from qilisdk.analog import Schedule, X, Z
from qilisdk.core import ket
from qilisdk.functionals.quantum_reservoirs import QuantumReservoir, ReservoirInput, ReservoirLayer
gain = ReservoirInput("gain", 0.2)
detuning = ReservoirInput("detuning", -0.1)
hamiltonian = gain * (X(0) + X(1)) + detuning * (Z(0) + Z(1)) + Z(0) * Z(1)
layer = ReservoirLayer(
evolution_dynamics=Schedule(
hamiltonians={"h": hamiltonian},
coefficients={"h": {(0.0, 1.0): 1.0}},
dt=0.05,
),
)
reservoir = QuantumReservoir(
initial_state=ket(0, 0),
reservoir_layer=layer,
input_per_layer=[
{"gain": 0.1, "detuning": -0.2},
{"gain": 0.7, "detuning": 0.0},
{"gain": 0.3, "detuning": 0.2},
],
)
3. Codificar en el perfil y la duración del schedule
from qilisdk.analog import Schedule, X, Z
from qilisdk.core import ket
from qilisdk.functionals.quantum_reservoirs import QuantumReservoir, ReservoirInput, ReservoirLayer
drive_amp = ReservoirInput("drive_amp", 1.0)
duration = ReservoirInput("duration", 1.0)
layer = ReservoirLayer(
evolution_dynamics=Schedule(
hamiltonians={
"drive": X(0) + X(1),
"problem": Z(0) * Z(1),
},
coefficients={
"drive": {(0.0, 1.0): drive_amp},
"problem": {(0.0, 1.0): lambda t: t},
},
total_time=duration,
dt=0.05,
),
)
reservoir = QuantumReservoir(
initial_state=ket(0, 0),
reservoir_layer=layer,
input_per_layer=[
{"drive_amp": 1.0, "duration": 0.6},
{"drive_amp": 0.3, "duration": 1.4},
],
)