Reservoris Quàntics

El funcional QuantumReservoir executa un pipeline de reservoir sobre una seqüència d’entrades. Cada capa aplica un circuit opcional de preprocessament, un bloc de dinàmica de reservoir analog (Schedule) i un circuit opcional de postprocessament, seguit de mesures d’un o més observables. La llista opcional qubits_to_reset es pot fer servir per restablir qubits seleccionats entre capes.

Les entrades del reservoir es representen fent servir paràmetres ReservoirInput. Aquests es comporten com els objectes estàndard Parameter però estan marcats com a no entrenables, de manera que poden ser dirigits per seqüències de dades d’entrada en lloc de bucles d’optimització.

Paràmetres

  • initial_state (QTensor): Estat inicial del reservoir.

  • reservoir_layer (ReservoirLayer): Defineix el pre/postprocessament, la dinàmica del reservoir, els observables i la política de restabliment.

  • input_per_layer (List[Dict[str, float]]): Valors d’entrada a aplicar a cada capa, indexats pels noms dels paràmetres d’entrada (normalment les etiquetes de ReservoirInput).

Retorna

Exemple d’ús

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ó de dades d’entrada

Podeu injectar dades clàssiques en una capa de reservoir en múltiples llocs. L’únic requisit és que les claus de input_per_layer coincideixin amb les etiquetes dels objectes ReservoirInput que col·loqueu a la capa.

1. Codificar amb circuits d’entrada i sortida

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 directament en paràmetres del Hamiltonià

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 i la durada de l’esquema

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},
    ],
)