Programas Variacionales

El funcional VariationalProgram reúne los componentes necesarios para un algoritmo cuántico variacional. Acepta un functional primitivo parametrizado, un optimizador y una función de coste. Al llamar a execute(), evalúa el functional repetidamente con parámetros actualizados, alimenta el FunctionalResult resultante en la función de coste suministrada y finalmente devuelve un VariationalProgramResult. Las restricciones de parámetros (desigualdades/igualdades sobre parámetros) se adjuntan en este nivel mediante el argumento parameter_constraints; este es el lugar para imponer relaciones como theta >= phi o límites entre parámetros en todos los functionals de QiliSDK. Solo los parámetros marcados como entrenables se optimizan durante este bucle.

Parámetros

  • functional (PrimitiveFunctional): Primitivo parametrizado a optimizar (por ejemplo, DigitalPropagation o AnalogEvolution).

  • optimizer (Optimizer): Optimizador clásico que propone nuevos valores de parámetros y opcionalmente almacena los iterados intermedios.

  • cost_function (CostFunction): Objeto que transforma los resultados del functional en un coste escalar; frecuentemente construido a partir de un Model.

  • store_intermediate_results (bool, opcional): Cuando es True, el optimizador conserva los pasos intermedios, que se exponen mediante intermediate_results.

  • parameter_constraints (list[ComparisonTerm], opcional): Restricciones sobre los parámetros del functional (p. ej., theta >= 0.5) evaluadas antes de cada actualización del optimizador. Este es el punto de entrada admitido para imponer relaciones entre parámetros en QiliSDK.

Retorna

Ejemplo de Uso (con el Backend QiliSim)

import numpy as np

from qilisdk.backends import QiliSim
from qilisdk.core.model import Model, ObjectiveSense
from qilisdk.core.variables import LEQ, BinaryVariable
from qilisdk.cost_functions.model_cost_function import ModelCostFunction
from qilisdk.digital import CNOT, U3, HardwareEfficientAnsatz
from qilisdk.functionals import DigitalPropagation
from qilisdk.functionals.variational_program import VariationalProgram
from qilisdk.optimizers.scipy_optimizer import SciPyOptimizer
from qilisdk.readout import Readout


values = [2, 3, 7]
weights = [1, 3, 3]
max_weight = 4
binary_var = [BinaryVariable(f"b{i}") for i in range(len(values))]

model = Model("Knapsack")

model.set_objective(sum(binary_var[i] * values[i] for i in range(len(values))), sense=ObjectiveSense.MAXIMIZE)

model.add_constraint("max_weights", LEQ(sum(binary_var[i] * weights[i] for i in range(len(weights))), max_weight))


ansatz = HardwareEfficientAnsatz(
    nqubits=3, layers=4, connectivity="Circular", one_qubit_gate=U3, two_qubit_gate=CNOT, structure="Interposed"
)

optimizer = SciPyOptimizer(method="COBYQA")

backend = QiliSim()
result = backend.execute(
    VariationalProgram(
        functional=DigitalPropagation(ansatz),
        optimizer=optimizer,
        cost_function=ModelCostFunction(model),
    ),
    Readout().with_sampling(nshots=1000),
)

print(result)

Salida

VariationalProgramResult(
  Optimal Cost=-9.0,
  Optimal Parameters=[...],
  Intermediate Results=[...],
  Optimal Results=- Functional Results: [

Sampling Results: (
    nshots=1000,
    samples={'000': 2, '010': 3, '101': 994, '110': 1}
)

]
)

Ejemplo de Uso 2 (con el Backend QiliSim) Este ejemplo optimiza un schedule variacional bajo ciertas restricciones de parámetros.

from qilisdk.core.variables import LT, GreaterThan
from qilisdk.cost_functions.observable_cost_function import ObservableCostFunction
from qilisdk.functionals import VariationalProgram, AnalogEvolution
from qilisdk.optimizers.scipy_optimizer import SciPyOptimizer
from qilisdk.analog import *
from qilisdk.analog.schedule import Interpolation
from qilisdk.core.variables import Parameter
from qilisdk.core import ket, tensor_prod
from qilisdk.backends import QiliSim
from qilisdk.readout import Readout
import numpy as np

from qilisdk.utils.visualization.style import ScheduleStyle


T = 10
p = [Parameter(f"p_{i}", (i + 1)*2, bounds=(0, 10)) for i in range(4)]
p.insert(0, 0)
p.append(T)
s = [Parameter(f"s_{i}", (i + 2) * 0.1, bounds=(0, 1)) for i in range(2)]
h0 = X(0)
h1 = Z(0)
max_time = Parameter("max_time", 1.5)

schedule = Schedule(
    hamiltonians={"h_x": h0, "h_z": h1},
    coefficients={
        "h_x": {p[0]: 1, (p[1], p[2]): 1 - s[0], (p[3], p[4]): 1 - s[1], p[5]: 0},
        "h_z": {p[0]: 0, (p[1], p[2]): s[0], (p[3], p[4]): s[1], p[5]: 1},
    },
    interpolation=Interpolation.LINEAR,
)

schedule.draw(ScheduleStyle(title="Schedule Before Optimization"))

te = AnalogEvolution(
    schedule=schedule,
    initial_state=tensor_prod([ket(0) - ket(1) for _ in range(schedule.nqubits)]).unit(),
)

vp = VariationalProgram(
    te,
    SciPyOptimizer(method="COBYQA"),
    cost_function=ObservableCostFunction(h1),
    parameter_constraints=[
        GreaterThan(p[3], 5)
    ]
)

print(vp.get_constraints()) # print the constraints of the variational program.

backend = QiliSim()
results = backend.execute(vp, Readout().with_expectation(observables=[h1]).with_state_tomography().with_sampling(nshots=1000))
schedule.draw(ScheduleStyle(title="Schedule After Optimization"))
print(results)