Constrained Quantum Optimizer: una función de Qiskit de Aqarios
Consulta la referencia de la API
Las Qiskit Functions son una función experimental disponible únicamente para los usuarios de los planes IBM Quantum® Premium Plan, Flex Plan y On-Prem (a través de la API de IBM Quantum Platform). Se encuentran en estado de versión preliminar y están sujetas a cambios.
Información general
Con el Constrained Quantum Optimizer de Aqarios, puedes resolver problemas de optimización binaria con restricciones en hardware cuántico de IBM® sin necesidad de reformulación manual ni de mapeo de circuitos. Simplemente proporciona un problema como archivo LP, archivo MPS o un Luna Model. La función se encarga automáticamente de la reformulación, la síntesis de circuitos, la transpilación y la ejecución en el hardware. El solucionador utiliza el arranque en caliente iterativo [1] del algoritmo FlexQAOA, una extensión del Quantum Approximate Optimization Algorithm (QAOA) que incorpora las restricciones de forma nativa.
El optimizador se centra en problemas binarios con restricciones. En particular, las restricciones one-hot y las restricciones de set-packing pueden aplicarse de forma nativa mediante el uso de mezcladores XY. Otros tipos de restricciones se reformulan automáticamente como términos de penalización.
La función resuelve únicamente problemas discretos. Toda variable de decisión debe ser binaria, o entera con un límite inferior y superior explícitos, de modo que pueda codificarse en qubits. Las variables continuas (de valor real) no se admiten: un modelo que las contenga se rechaza con el código de error 4710.
Descripción
El rendimiento puede depender tanto de la instancia del problema como de los pasos de procesamiento posteriores. En algunos casos, las muestras clásicas y las muestras generadas cuánticamente pueden alcanzar una calidad de solución final similar después de un posprocesamiento equivalente. Por lo tanto, la evaluación debe considerar el flujo de trabajo de optimización completo.
El Constrained Quantum Optimizer implementa el Iterative warm-start XY-mixer QAOA (IWS-QAOA) [1]. El algoritmo ejecuta circuitos de QAOA de ángulo fijo en hardware cuántico y actualiza las probabilidades iniciales de arranque en caliente de cada circuito utilizando los resultados de medición de la iteración anterior. No se requiere entrenamiento de parámetros variacionales; en su lugar, se elige una parametrización de rampa lineal fija.
Al utilizar mezcladores XY para aplicar restricciones one-hot, puedes mejorar drásticamente la calidad de la solución en problemas con restricciones, ya que el espacio de búsqueda se reduce prácticamente a un subespacio factible más pequeño — y no se introduce complejidad adicional, debida a los términos de penalización, en el panorama de optimización.
Flujo de trabajo
El flujo de trabajo de la función se describe a continuación:
-
Preprocesamiento: Antes de la síntesis del circuito, el optimizador analiza la estructura de restricciones del problema de entrada:
-
Preprocesamiento de grafos: Cuando es aplicable al problema, este se analiza como un grafo, donde las aristas determinan la relación entre las variables. Este análisis de grafos permite dos mejoras de preprocesamiento del problema. Primero, las restricciones individuales pueden agregarse en una única restricción grande mediante la fusión de cliqués (clique merging), de modo que se representen mediante un único mezclador XY. Segundo, las variables que tienen un único vecino pueden fijarse de forma determinista, reduciendo el tamaño y la complejidad del problema.
-
Resolución de restricciones: El preprocesador maneja cada restricción por separado: si la restricción es una restricción one-hot, puede aplicarse mediante mezcladores XY. Si es una restricción de set-packing, primero se transforma en una restricción one-hot. Las restricciones de igualdad se manejan mediante penalizaciones cuadráticas, y las restricciones de desigualdad mediante penalización no balanceada.
-
Derivación de penalizaciones: Las restricciones que no pueden expresarse como restricciones de igualdad one-hot se convierten en términos de penalización. El optimizador deriva automáticamente la intensidad de la penalización a partir de la estructura del problema.
-
-
Arranque en caliente iterativo: El optimizador ejecuta múltiples cadenas paralelas de arranque en caliente. Cada cadena mantiene una distribución de probabilidad sobre las asignaciones de variables, que se utiliza como la probabilidad inicial del circuito de QAOA (estado de arranque en caliente). En cada iteración, una cadena muestrea el circuito cuántico, aplica posprocesamiento clásico y actualiza su distribución en función de la aparición de las muestras. A lo largo de las iteraciones, esto centra la búsqueda en soluciones de alta calidad sin un bucle de optimización variacional.
El número de cadenas (num_parallel), el presupuesto total de shots por cadena (total_shots) y el número de repeticiones de QAOA (reps) son los principales controles para equilibrar la calidad de la solución frente al tiempo de ejecución.
Benchmarks
Los siguientes benchmarks muestran que la Qiskit Function Constrained Quantum Optimizer es capaz de encontrar soluciones factibles de alta calidad para problemas de optimización con restricciones con bastante más de 100 variables binarias. La precisión (Accuracy) es la relación entre el valor objetivo encontrado y el óptimo conocido. El tiempo de ejecución (Runtime) es el tiempo real transcurrido desde la invocación de la función hasta la entrega del resultado. El uso de QPU es el tiempo de cómputo cuántico facturable.
Se examinaron dos problemas: el problema del Conjunto Independiente Máximo (MIS) y el Max--Cut, que es una extensión del conocido problema MaxCut a más de dos categorías. Max--Cut es también el equivalente, como problema de optimización, del problema de decisión de coloreado de grafos (cuando se cortan todas las aristas, el grafo es -coloreable).
| Problema | Variables | Restricciones | Qubits | Precisión | Factibilidad | Tiempo total de ejecución (s) | Uso de QPU (s) | Total de shots |
|---|---|---|---|---|---|---|---|---|
| MIS es60fst02 | 186 | 280 | 124 | 100.0% | Verdadero | 940.2 | 147.0 | 25.0k |
| MIS sloane_2dc_128 | 128 | 5173 | 74 | 100.0% | Verdadero | 347.6 | 106.0 | 12.5k |
| Max-3-Cut Karate Club | 99 | 33 | 99 | 98.7% | Verdadero | 279.5 | 166.0 | 25.0k |
| Max-4-Cut Karate Club | 132 | 33 | 132 | 100.0% | Verdadero | 258.3 | 200.0 | 25.0k |
Notas de los benchmarks
-
Los resultados anteriores se obtuvieron en el procesador Heron r2
ibm_marrakeshcon las opciones predeterminadas yuse_sessionhabilitado. Para la instanciasloane_2dc_128, se utilizótotal_shots=12500. -
El número reducido de qubits para las instancias de MIS (74 y 124 frente a 128 y 186 variables) refleja la fijación de variables durante el preprocesamiento.
-
Las instancias del problema para MIS se toman de la Quantum Optimization Benchmarking Library. - La instancia para el problema Max--Cut es el grafo del Zachary Karate Club, que tiene un número cromático de 5.
Primeros pasos
Esta guía demuestra el uso básico del Constrained Quantum Optimizer mostrando cómo cargar la función y ejecutar problemas simples. Para un tutorial más detallado, consulta Encuentra el conjunto independiente máximo con el Constrained Quantum Optimizer de Aqarios.
Autentícate con tu clave de API de IBM Quantum y el CRN de la instancia, luego carga la función desde el catálogo.
El siguiente código supone que ya has guardado tus credenciales. Para configurarlas, sigue las instrucciones en Guarda las credenciales de tu cuenta de IBM Cloud.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"luna_model": "luna-model", "networkx": "networkx", "qiskit_addon_opt_mapper": "qiskit-addon-opt-mapper", "qiskit_ibm_catalog": "qiskit-ibm-catalog"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Verify that you have access to the function
catalog.list()
[QiskitFunction(aqarios/constrained-quantum-optimizer)]
# Load the function
optimizer = catalog.load("aqarios/constrained-quantum-optimizer")
# Check the list of backends you have access to
catalog.backends()
[<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_boston')>,
<IBMBackend('ibm_phoenix')>,
<IBMBackend('ibm_fez')>,
<IBMBackend('ibm_miami')>,
<IBMBackend('ibm_marrakesh')>,
<IBMBackend('ibm_kingston')>]
# Select the backend you want to use
backend = catalog.backend("ibm_phoenix")
Ejemplo 1: Un problema simple con restricciones
El primer ejemplo muestra cómo usar la función con archivos LP, una especificación común de problemas de optimización utilizada en investigación operativa. Formularás el siguiente problema de optimización arbitrario de cinco variables binarias sujeto a dos restricciones:
1. Formula el problema
Indica directamente la expresión matemática con el siguiente formato de cadena LP:
lp_str = """\\Problem name: Example
Minimize
obj: [ 2 x_1 x_2 + 4 x_2 x_3 - 2 x_3 x_4 - 4 x_4 x_5 - 4 x_1 x_3 - 8 x_2 x_5 ] / 2
Subject To
c1: x_1 + x_2 + x_3 <= 1
c2: x_3 - x_4 + x_5 = 0
Binaries
x_1 x_2 x_3 x_4 x_5
End
"""
Ten en cuenta que [ ... ] / 2 es una notación requerida para los objetivos cuadráticos según el estándar LP.
2. Ejecuta el optimizador
Resuelve el problema con el optimizador.
# Set up low-resource options for the simple problem
options = {"total_shots": 2000, "num_parallel": 5, "use_session": True}
# Dispatch the job
job_1 = optimizer.run(
problem=lp_str, backend_name=backend.name, options=options
)
print(f"Job ID: {job_1.job_id}")
Job ID: 2e06a3a8-7c29-4c76-94d7-194293363f4a
# Monitor the job status
job_1.status()
'RUNNING: WAITING_FOR_QPU'
3. Obtén el resultado
El resultado proporciona la solución, que asigna a cada nombre de variable su valor asociado.
result = job_1.result()
result
{'solutions': [{'x_1': 0, 'x_2': 1, 'x_3': 0, 'x_4': 1, 'x_5': 1}],
'solution_bitstrings': ['01011'],
'raw_energy': -6.0,
'objective_value': -6.0,
'feasible': True,
'metadata': {'resource_usage': {'RUNNING: MAPPING': {'CPU': 5.405},
'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU': 9.902},
'RUNNING: WAITING_FOR_QPU': {'CPU': 25.456},
'RUNNING: EXECUTING_QPU': {'CPU': 25.202},
'RUNNING: POST_PROCESSING': {'CPU': 6.481}},
'circuit_metrics': {'before_transpilation': {'num_qubits': 6,
'gate_count': 42,
'depth': 15,
'two_qubit_gate_count': 12,
'two_qubit_gate_depth': 7,
'operations': {'rz': 12,
'p': 8,
'ry': 7,
'rzz': 4,
'cx': 3,
'xx_plus_yy': 3,
'global_phase': 2,
'cry': 2,
'x': 1}},
'after_transpilation': {'num_qubits': 6,
'gate_count': 254,
'depth': 100,
'two_qubit_gate_count': 48,
'two_qubit_gate_depth': 33,
'operations': {'rz': 78, 'sx': 122, 'cz': 48, 'measure': 6, 'x': 0}}},
'solver_info': {'variable_mapping': {'x_1': 0,
'x_2': 1,
'x_3': 2,
'x_4': 3,
'x_5': 4}}}}
El campo solutions del diccionario devuelto contiene todas las mejores soluciones degeneradas encontradas, como diccionarios que asignan a cada nombre de variable su valor. Las mismas soluciones también se devuelven como cadenas de bits en solution_bitstrings; metadata.solver_info.variable_mapping te indica qué posición de carácter de una cadena de bits pertenece a cada variable. Junto con los datos de la solución, se proporcionan el objective_value, un indicador feasible y metadata adicionales.
Ejemplo 2: Conjunto independiente máximo
El problema del Conjunto Independiente Máximo (MIS) busca el subconjunto más grande de vértices de un grafo tal que ningún par de vértices del subconjunto comparta una arista. Este ejemplo resuelve el MIS en un grafo regular de 12 nodos. Para más información sobre el problema y un ejemplo más extenso, consulta el tutorial.
El problema se construye con qiskit-addon-opt-mapper. Instala los paquetes necesarios si es preciso:
# %pip install networkx qiskit-addon-opt-mapper
Paso 1: Define el problema
Construye un grafo aleatorio 3-regular y crea el problema de optimización MIS. Cada nodo del grafo se convierte en una variable binaria, y cada arista introduce una restricción que garantiza que como máximo se seleccione uno de sus extremos.
import networkx as nx
from qiskit_addon_opt_mapper.applications import IndependentSet
from qiskit_addon_opt_mapper.translators import to_docplex_mp
# 12-node random 3-regular graph
G = nx.random_regular_graph(3, 12, seed=7)
# Build MIS problem and export as LP string
mis = IndependentSet(G)
mis_problem = mis.to_optimization_problem()
lp_str = to_docplex_mp(mis_problem).export_as_lp_string()
print(lp_str)
\ This file has been generated by DOcplex
\ ENCODING=ISO-8859-1
\Problem name: Independent set
Maximize
obj: x_0 + x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7 + x_8 + x_9 + x_10 + x_11
Subject To
c0: x_0 + x_4 <= 1
c1: x_0 + x_9 <= 1
c2: x_0 + x_8 <= 1
c3: x_1 + x_2 <= 1
c4: x_1 + x_7 <= 1
c5: x_1 + x_3 <= 1
c6: x_2 + x_4 <= 1
c7: x_2 + x_11 <= 1
c8: x_3 + x_10 <= 1
c9: x_3 + x_11 <= 1
c10: x_4 + x_5 <= 1
c11: x_5 + x_10 <= 1
c12: x_5 + x_6 <= 1
c13: x_6 + x_8 <= 1
c14: x_6 + x_7 <= 1
c15: x_7 + x_9 <= 1
c16: x_8 + x_11 <= 1
c17: x_9 + x_10 <= 1
Bounds
0 <= x_0 <= 1
0 <= x_1 <= 1
0 <= x_2 <= 1
0 <= x_3 <= 1
0 <= x_4 <= 1
0 <= x_5 <= 1
0 <= x_6 <= 1
0 <= x_7 <= 1
0 <= x_8 <= 1
0 <= x_9 <= 1
0 <= x_10 <= 1
0 <= x_11 <= 1
Binaries
x_0 x_1 x_2 x_3 x_4 x_5 x_6 x_7 x_8 x_9 x_10 x_11
End
Paso 2: Ejecuta el optimizador
Resuelve el problema con el optimizador.
options = {"total_shots": 2000, "use_session": True}
# Dispatch the job
job_2 = optimizer.run(
problem=lp_str, backend_name=backend.name, options=options
)
print(f"Job ID: {job_2.job_id}")
Job ID: f041431c-b42e-4627-afe5-522fea61faf6
# Monitor the job status
job_2.status()
'QUEUED'
Paso 3: Obtén e interpreta los resultados
Por último, obtenemos la solución y comprobamos que es correcta.
result = job_2.result()
print(f"Feasible: {result['feasible']}")
print(f"Independent set size: {int(result['objective_value'])}")
# Extract the selected nodes
solution = result["solutions"][0]
independent_set = [
int(k.split("_")[1]) for k, v in solution.items() if v == 1
]
print(f"Selected nodes: {sorted(independent_set)}")
# Verify no two selected nodes share an edge
conflicts = [
(u, v)
for u, v in G.edges()
if u in independent_set and v in independent_set
]
print(f"Edge conflicts: {conflicts}")
Feasible: True
Independent set size: 5
Selected nodes: [1, 4, 6, 9, 11]
Edge conflicts: []
Ejemplo 3: Coloreado de grafos con 3 colores (Max-3-Cut)
El problema Max--Cut divide los vértices de un grafo en grupos para maximizar el número de aristas cuyos extremos pertenecen a grupos diferentes. Para , esto equivale a encontrar un coloreado de 3 colores que maximice las aristas entre colores distintos.
Formalmente, se asigna cada nodo a uno de los grupos mediante variables binarias para el color . La restricción one-hot para cada nodo garantiza una asignación única. El objetivo maximiza las aristas entre grupos:
Las restricciones one-hot son ideales para los mezcladores XY, que el optimizador maneja de forma nativa.
Este ejemplo utiliza el grafo del Zachary Karate Club (34 nodos, 78 aristas) y construye el modelo con el Luna Model. Instala los paquetes necesarios si es preciso:
# %pip install networkx matplotlib luna-model
Paso 1: Define el problema
Primero, genera las variables binarias con el paquete luna-model. Luego, define el objetivo y agrega las restricciones al modelo.
import networkx as nx
from luna_model import Model, quicksum
k = 3
G = nx.karate_club_graph()
# Instantiate the Model object
model = Model(name="Max-3-Cut")
# Add binary variables
x = model.add_variables("x", shape=(G.number_of_nodes(), k))
# Add the objective by using the numpy dot abstraction
model.objective = quicksum(x[i].dot(x[j]) for i, j in G.edges)
# Bulk-add the one-hot constraints
model.add_constraints(x.sum(axis=1) == 1)
# Encode the model to send via API
model_enc = model.encode_b64()
Paso 2: Ejecuta el optimizador
Resuelve el problema con el optimizador.
options = {"use_session": True}
# Dispatch the job
job_3 = optimizer.run(
problem=model_enc, backend_name=backend.name, options=options
)
print(f"Job ID: {job_3.job_id}")
Job ID: df911d83-9df7-44a1-bfad-5d66e08676c3
# Monitor the job status
job_3.status()
'RUNNING: WAITING_FOR_QPU'
Paso 3: Obtén y visualiza los resultados
Obtén y visualiza los resultados.
result = job_3.result()
solution = result["solutions"][0]
# Map each node to its assigned color group
node_group = {}
for i in G.nodes:
for c in range(k):
if solution[f"x{i},{c}"] == 1:
node_group[i] = c
print(f"Feasible: {result['feasible']}")
print(f"Violations: {int(result['objective_value'])}")
# Visualize the partition
colors = ["tab:blue", "tab:orange", "tab:green"]
node_colors = [colors[node_group[i]] for i in G.nodes()]
nx.draw(G, nx.kamada_kawai_layout(G), node_color=node_colors, node_size=200)
Feasible: True
Violations: 4

Registro de cambios
[0.4.0] - 2026-09-22
Cambios incompatibles
- Se renombró el parámetro de entrada
modelaproblem. - Se renombró el campo de salida
obj_valueaobjective_value. - Se reemplazó la opción
postprocessingporpostprocessing_level, que toma un entero en el rango[0, 3]en lugar de una cadena. Los valores anteriores se corresponden con"off"->0,"weak"->1,"medium"->2y"strong"->3, y el valor predeterminado no cambia y sigue siendo3. - Se reestructuró
metadata.circuit_metrics. Ahora informanum_qubits,gate_count,depth,two_qubit_gate_count,two_qubit_gate_depthyoperationspor separado parabefore_transpilationyafter_transpilation, en lugar de un único diccionario plano de recuentos promedio de compuertas. El campo esNonecuando el problema se resolvió durante el preprocesamiento y no se ejecutó ningún circuito (código de error4712).
Características
- Se añadió la opción
seed_transpilerpara hacer que la síntesis y el enrutamiento de circuitos sean reproducibles entre ejecuciones. - Se añadió la opción
job_tagspara adjuntar tus propias etiquetas a cada trabajo de Qiskit Runtime que envía la función. - Se añadió el campo de salida
solution_bitstrings, que devuelve las mismas soluciones quesolutionsen forma de cadenas de bits. - Se añadió
metadata.solver_info.variable_mapping, que asigna a cada nombre de variable su posición de carácter en las entradas desolution_bitstrings.
Documentación
- Se documentó que las variables continuas (de valor real) no se admiten y que las variables enteras requieren límites explícitos. Dichos modelos fallan con el código de error
4710.
Obtén ayuda
Para preguntas o problemas, contacta con support@aqarios.com indicando tu ID de trabajo.
Próximos pasos
- Solicita acceso al Constrained Quantum Optimizer de Aqarios.
- Visita la referencia de la API para la documentación completa de parámetros y salidas.
- Prueba el tutorial Encuentra el conjunto independiente máximo con el Constrained Quantum Optimizer de Aqarios para profundizar en una instancia real de benchmark de QOBLIB.
- Revisa la publicación Constrained Quantum Optimization via Iterative Warm-Start XY-Mixers.
Referencias
- Bucher et al. (2026), Constrained Quantum Optimization via Iterative Warm-Start XY-Mixers 10.1088/1367-2630/ae8ea2