Optimization Solver: una Qiskit Function de Q-CTRL Fire Opal
Consulta la referencia de API
Las Qiskit Functions son una función experimental disponible únicamente para usuarios del Plan Premium, el Plan Flex y el Plan On-Prem (a través de la API de IBM Quantum Platform) de IBM Quantum®. Se encuentran en estado de versión preliminar y están sujetas a cambios.
Versiones de paquetes
El código de esta página fue desarrollado con los siguientes requisitos. Recomendamos usar estas versiones o más recientes.
qiskit-ibm-runtime~=0.46.1
sympy~=1.14.0
Descripción general
Con el Fire Opal Optimization Solver, puedes resolver problemas de optimización a escala utilitaria en hardware cuántico sin necesidad de experiencia en computación cuántica. Simplemente introduce la definición del problema de alto nivel y el Solver se encarga del resto. Todo el flujo de trabajo es consciente del ruido y aprovecha la Gestión del rendimiento de Fire Opal internamente. El Solver ofrece de manera consistente soluciones precisas a problemas difíciles de resolver de forma clásica, incluso a escala de dispositivo completo en los QPUs IBM® más grandes.
El Solver es flexible y puede utilizarse para resolver problemas de optimización combinatoria definidos como funciones objetivo o grafos arbitrarios. Los problemas no tienen que ser mapeados a la topología del dispositivo. Tanto los problemas sin restricciones como los restringidos son solucionables, siempre que las restricciones puedan formularse como términos de penalización. Los ejemplos incluidos en esta guía demuestran cómo resolver un problema de optimización a escala utilitaria sin restricciones y uno con restricciones, usando diferentes tipos de entrada del Solver. El primer ejemplo involucra un problema max-cut definido en un grafo 3-regular de 156 nodos, mientras que el segundo aborda un problema de Cobertura Mínima de Vértices de 50 nodos definido por una función de costo.
Para obtener acceso al Optimization Solver, contacta a Q-CTRL.
Descripción de la función
El Solver optimiza y automatiza por completo todo el algoritmo, desde la supresión de errores a nivel de hardware hasta el mapeo eficiente del problema y la optimización clásica en bucle cerrado. En segundo plano, el pipeline del Solver reduce los errores en cada etapa, habilitando el rendimiento mejorado necesario para escalar de manera significativa. El flujo de trabajo subyacente está inspirado en el Quantum Approximate Optimization Algorithm (QAOA), que es un algoritmo híbrido cuántico-clásico. Para un resumen detallado del flujo de trabajo completo del Optimization Solver, consulta el manuscrito publicado.
Para resolver un problema genérico con el Optimization Solver:
- Define tu problema como una función objetivo, un grafo o una cadena de espín
SparsePauliOp. - Conéctate a la función a través del Catálogo de Funciones de Qiskit.
- Ejecuta el problema con el Solver y recupera los resultados.
Formatos de problema aceptados
- Representación en expresión polinómica de una función objetivo. Idealmente creada en Python con un objeto SymPy Poly existente y formateada como cadena de texto usando sympy.srepr.
- Representación en grafo de un tipo de problema específico. El grafo debe crearse usando la librería networkx en Python. Luego debe convertirse a cadena de texto usando la función de networkx
[nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.). - Representación en cadena de espín de un problema específico. La cadena de espín debe representarse como un objeto
SparsePauliOp; consulta la documentación para más detalles.
Backends soportados
Ejecuta el siguiente código para ver la lista de backends actualmente soportados. Si tu dispositivo no aparece en la lista, contacta a Q-CTRL para añadir soporte.
# Added by doQumentation — required packages for this notebook
!pip install -q networkx numpy qiskit-ibm-catalog qiskit-ibm-runtime sympy
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
[<IBMBackend('ibm_boston')>,
<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_fez')>,
<IBMBackend('ibm_marrakesh')>,
<IBMBackend('ibm_kingston')>,
<IBMBackend('ibm_miami')>]
Benchmarks
Los resultados de benchmarking publicados muestran que el Solver resuelve con éxito problemas con más de 120 qubits, superando incluso resultados previamente publicados en recocido cuántico y dispositivos de iones atrapados. Las siguientes métricas de benchmark ofrecen una indicación aproximada de la precisión y escalabilidad de los tipos de problemas basándose en algunos ejemplos. Las métricas reales pueden variar según diversas características del problema, como el número de términos en la función objetivo (densidad) y su localidad, el número de variables y el orden polinómico.
El "Número de qubits" indicado no es una limitación estricta, sino que representa umbrales aproximados donde puedes esperar una precisión de solución extremadamente consistente. Se han resuelto con éxito problemas de mayor tamaño, y se alienta a hacer pruebas más allá de estos límites.
La conectividad arbitraria de qubits está soportada en todos los tipos de problemas.
| Tipo de problema | Número de qubits | Ejemplo | Precisión | Tiempo total (s) | Uso de runtime (s) | Número de iteraciones |
|---|---|---|---|---|---|---|
| Problemas cuadráticos con conectividad dispersa | 156 | max-cut 3-regular | 100% | 1764 | 293 | 16 |
| Optimización binaria de orden superior | 156 | Modelo de espín-vidrio de Ising | 100% | 1461 | 272 | 16 |
| Problemas cuadráticos con conectividad densa | 50 | max-cut completamente conectado | 100% | 1758 | 268 | 12 |
| Problema restringido con términos de penalización | 50 | Cobertura Mínima de Vértices ponderada con 8% de densidad de aristas | 100% | 1074 | 215 | 10 |
Primeros pasos
Primero, autentícate usando tu clave API de IBM Quantum. Luego, selecciona la Qiskit Function de la siguiente manera. (Este fragmento de código asume que ya has guardado tu cuenta en tu entorno local.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access Function
solver = catalog.load("q-ctrl/optimization-solver")
Ejemplo: Optimización sin restricciones
Ejecuta el problema de corte máximo (max-cut). El siguiente ejemplo demuestra las capacidades del Solver en un problema max-cut de grafo 3-regular no ponderado de 156 nodos, aunque también puedes resolver problemas de grafos ponderados.
Además de qiskit-ibm-catalog, también necesitarás los siguientes paquetes para ejecutar este ejemplo: networkx y numpy. Puedes instalarlos descomentando la siguiente celda si estás ejecutando este ejemplo en un notebook con el kernel de IPython.
# %pip install networkx numpy
1. Define el problema
Puedes ejecutar un problema max-cut definiendo un problema de grafo y especificando problem_type='maxcut'.
import networkx as nx
import numpy as np
# Generate a random graph with 156 nodes
maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)
# Optionally, visualize the graph
nx.draw_networkx(
maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100
)
El Solver acepta una cadena de texto como entrada para la definición del problema.
# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)
2. Ejecuta el problema
Cuando uses el método de entrada basado en grafos, especifica el tipo de problema.
# This cell is hidden from users
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend_name = service.least_busy(n_qubits=156).name
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)
Comprueba el estado de la carga de trabajo de tu Qiskit Function o recupera los resultados de la siguiente manera:
# Get job status
print(maxcut_job.status())
QUEUED
3. Recupera el resultado
Recupera el valor de corte óptimo del diccionario de resultados.
El mapeo de las variables a la cadena de bits puede haber cambiado. El diccionario de salida contiene un subdicionario variables_to_bitstring_index_map que ayuda a verificar el orden.
# Poll for results
maxcut_result = maxcut_job.result()
# Take the absolute value of the solution since the cost function is minimized
qctrl_maxcut = abs(maxcut_result["solution_bitstring_cost"])
# Print the optimal cut value found by the Optimization Solver
print(f"Optimal cut value: {qctrl_maxcut}")
Optimal cut value: 210.0
Puedes verificar la precisión del resultado resolviendo el problema de forma clásica con solvers de código abierto como PuLP si el grafo no tiene conectividad densa. Los problemas de alta densidad pueden requerir solvers clásicos avanzados para validar la solución.
Ejemplo: Optimización con restricciones
El ejemplo anterior de max-cut es un problema de optimización binaria cuadrática sin restricciones común. El Optimization Solver de Q-CTRL puede usarse para distintos tipos de problemas, incluida la optimización con restricciones. Puedes resolver tipos de problemas arbitrarios introduciendo la definición del problema representada como un polinomio donde las restricciones se modelan como términos de penalización.
El siguiente ejemplo demuestra cómo construir una función de costo para un problema de optimización con restricciones: la cobertura mínima de vértices (MVC, por sus siglas en inglés).
Además de los paquetes qiskit-ibm-catalog y qiskit, también necesitarás los siguientes paquetes para ejecutar este ejemplo: numpy, networkx y sympy. Puedes instalarlos descomentando la siguiente celda si estás ejecutando este ejemplo en un notebook con el kernel de IPython.
# %pip install numpy networkx sympy
1. Define el problema
Define un problema MVC aleatorio generando un grafo con nodos de peso aleatorio.
import networkx as nx
from sympy import symbols, Poly, srepr
# To change the weights, change the seed to any integer.
rng_seed = 18
_rng = np.random.default_rng(rng_seed)
node_count = 50
edge_probability = 0.08
mvc_graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)
# add node weights
for i in mvc_graph.nodes:
mvc_graph.add_node(i, weight=_rng.random())
# Optionally, visualize the graph
nx.draw_networkx(mvc_graph, nx.kamada_kawai_layout(mvc_graph), node_size=200)
Un modelo de optimización estándar para MVC ponderado puede formularse de la siguiente manera. Primero, debe añadirse una penalización para cualquier caso en que una arista no esté conectada a un vértice del subconjunto. Por lo tanto, sea si el vértice está en la cobertura (es decir, en el subconjunto) y en caso contrario. Segundo, el objetivo es minimizar el número total de vértices en el subconjunto, lo cual puede representarse mediante la siguiente función:
# Construct the cost function.
variables = symbols([f"n[{i}]" for i in range(node_count)])
cost_function = Poly(0, variables)
for i in mvc_graph.nodes():
weight = mvc_graph.nodes[i].get("weight", 0)
cost_function += variables[i] * weight
Ahora, cada arista del grafo debe incluir al menos un extremo de la cobertura, lo cual puede expresarse como la desigualdad:
Cualquier caso en que una arista no esté conectada al vértice de la cobertura debe ser penalizado. Esto puede representarse en la función de costo añadiendo una penalización de la forma donde es una constante de penalización positiva. Por lo tanto, una alternativa sin restricciones a la desigualdad restringida para MVC ponderado es:
# Add penalty term.
penalty_constant = 2
for i, j in mvc_graph.edges():
cost_function += penalty_constant * (
1 - variables[i] - variables[j] + variables[i] * variables[j]
)
2. Ejecuta el problema
# Solve the problem
mvc_job = solver.run(
problem=srepr(cost_function),
backend_name=backend_name, # E.g. "ibm_fez"
)
Comprueba el estado de la carga de trabajo de tu Qiskit Function o recupera los resultados de la siguiente manera:
print(mvc_job.status())
QUEUED
3. Obtén el resultado
Recupera la solución y analiza los resultados. Como este problema tiene nodos ponderados, la solución no es simplemente el número mínimo de nodos cubiertos. En cambio, el costo de la solución representa la suma de los pesos de los vértices incluidos en la cobertura de vértices. Representa el "costo" o "peso" total de cubrir todas las aristas del grafo utilizando los vértices seleccionados.
mvc_result = mvc_job.result()
qctrl_cost = mvc_result["solution_bitstring_cost"]
# Print results
print(f"Solution cost: {qctrl_cost}")
Solution cost: 10.248198273708624
Obtener soporte
Para cualquier pregunta o problema, contacta a Q-CTRL.
Historial de cambios
- 2026-02-11: Ahora se admite
ibm_miami.
Próximos pasos
- Solicita acceso al Q-CTRL Optimization Solver.
- Visita la referencia de API de esta Qiskit Function.
- Prueba el tutorial Resuelve problemas de optimización binaria de orden superior con el Optimization Solver de Q-CTRL.
- Revisa Sachdeva, N., et al. (2024). Quantum optimization using a 127-qubit gate-model IBM quantum computer can outperform quantum annealers for nontrivial binary optimization problems. arXiv preprint arXiv:2406.01743.
- Revisa Loco, D., et al. (2026). Practical protein-pocket hydration-site prediction for drug discovery on a quantum computer. arXiv preprint arXiv:2512.08390.
- Revisa el caso de estudio de Mazda.
- Revisa el caso de estudio de Network Rail.
- Revisa el caso de estudio del Ejército Australiano.
- Revisa el caso de estudio de Transport for New South Wales.