Skip to main content
IBM Quantum Platform

Solucionador de optimización: una función Qiskit de Q-CTRL Fire Opal

Consulte la referencia de la API

Note

Las funciones Qiskit 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 IBM Quantum Platform API). Se trata de versiones preliminares sujetas a cambios.

  • El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o posteriores.

    qiskit-ibm-runtime~=0.47.0
    sympy~=1.14.0
    

Visión general

Con el solucionador de optimización Fire Opal, puede resolver problemas de optimización a gran escala en hardware cuántico sin necesidad de conocimientos cuánticos. Basta con introducir la definición del problema de alto nivel y el Solver se encarga del resto. Todo el flujo de trabajo tiene en cuenta el ruido y aprovecha la gestión del rendimiento de Fire Opal. El Solver ofrece sistemáticamente soluciones precisas a los problemas clásicos más complejos, incluso a escala de dispositivo completo en las mayores QPU de IBM®.

El Solver es flexible y se puede utilizar para resolver problemas de optimización combinatoria definidos como funciones objetivo o grafos arbitrarios. No es necesario asignar los problemas a la topología del dispositivo. Tanto los problemas sin restricciones como los que tienen restricciones son resolubles, siempre que las restricciones puedan formularse como términos de penalización. Los ejemplos incluidos en esta guía muestran cómo resolver un problema de optimización a escala industrial, tanto sin restricciones como con restricciones, utilizando diferentes tipos de datos de entrada para el Solver. El primer ejemplo se refiere a un problema de corte máximo definido en un grafo regular de 3 con 156 nodos, mientras que el segundo ejemplo aborda un problema de cobertura mínima de vértices con 50 nodos definido mediante una función de coste.

Para acceder al Optimization Solver, póngase en contacto con 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 la asignación eficaz de problemas y la optimización clásica en bucle cerrado. Entre bastidores, la canalización del Solver reduce los errores en cada etapa, lo que permite mejorar el rendimiento necesario para escalar de forma significativa. El flujo de trabajo subyacente se inspira en el Algoritmo de Optimización Cuántica Aproximada (QAOA), que es un algoritmo híbrido cuántico-clásico. Para un resumen detallado del flujo de trabajo completo del Optimization Solver, consulte el manuscrito publicado.

Visualización del flujo de trabajo del Optimization Solver

Para resolver un problema genérico con el Optimization Solver:

  1. Defina su problema como una función objetivo, un gráfico o una cadena de spin SparsePauliOp .
  2. Conéctese a la función a través del Catálogo de Funciones Qiskit.
  3. Ejecute el problema con el Solver y obtenga los resultados.

Formatos de problemas admitidos

  • Representación de la expresión polinómica de una función objetivo. Idealmente creado en Python con un objeto SymPy Poly existente y formateado en una cadena utilizando sympy.srepr.
  • Representación gráfica de un tipo de problema específico. El gráfico debe crearse utilizando la biblioteca networkx en Python. A continuación, debe convertirse en una cadena utilizando la función networkx [nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.).
  • Representación en cadena de un problema concreto. La cadena de giro debe representarse como un objeto SparsePauliOp ; consulte la documentación para obtener más detalles.
¿Esta función es compatible con todos los servidores de fondo de IBM?

Si deseas utilizar un backend que esta función no admite actualmente, ponte en contacto con Q-CTRL para que lo incorporen.


Referencias comparativas

Los resultados de pruebas comparativas publicadas demuestran que el Solver resuelve con éxito problemas con más de 120 qubits, superando incluso resultados publicados anteriormente sobre recocido cuántico y dispositivos de iones atrapados. Las siguientes métricas de referencia proporcionan una indicación aproximada de la precisión y el escalado de los tipos de problemas basándose en algunos ejemplos. Las métricas reales pueden diferir en función de varias características del problema, como el número de términos de la función objetivo (densidad) y su localización, el número de variables y el orden polinómico.

El "Número de qubits" indicado no es una limitación rígida, sino que representa umbrales aproximados en los que se puede esperar una precisión de solución extremadamente consistente. Se han resuelto con éxito problemas de mayor envergadura, por lo que se recomienda realizar pruebas más allá de estos límites.

Se admite la conectividad arbitraria de qubits en todos los tipos de problemas.

Tipo de problema
Número de qubits
Ejemplo
Precisión
Tiempo total (s)
Tiempo de ejecución (s)
Número de iteraciones
Problemas cuadráticos de conexión dispersa1563-corte máximo regular100 %176429316
Optimización binaria de orden superior156Modelo Ising de espín-cristal100 %146127216
Problemas cuadráticos densamente conectados50Max-Cut totalmente conectado100 %17582686
Problema restringido con términos de penalización50Cubierta de vértices mínima ponderada con una densidad de aristas del 8100 %107421510

Cómo empezar

Primero, autentíquese utilizando su clave API IBM Quantum. A continuación, selecciona la función de Qiskit de la siguiente manera. (En este fragmento de código se da por hecho que ya has guardado tu cuenta en tu entorno local.)

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Verify that you have access to the function
catalog.list()

Output:

[QiskitFunction(qunova/hivqe-chemistry),
 QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
 QiskitFunction(algorithmiq/tem),
 QiskitFunction(qedma/qesem),
 QiskitFunction(multiverse/singularity),
 QiskitFunction(ibm/circuit-function),
 QiskitFunction(q-ctrl/optimization-solver),
 QiskitFunction(colibritd/quick-pde),
 QiskitFunction(q-ctrl/performance-management),
 QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# Access Function
solver = catalog.load("q-ctrl/optimization-solver")

Ejemplo: Optimización sin restricciones

Resuelve el problema del corte máximo (max-cut). El siguiente ejemplo muestra las capacidades del Solver en un problema de corte máximo en un grafo no ponderado de 156 nodos y 3 aristas regulares, aunque también se pueden resolver problemas en grafos ponderados.

Además de qiskit-ibm-catalog, también utilizará los siguientes paquetes para ejecutar este ejemplo: networkx y numpy. Puedes instalar estos paquetes descomentando la siguiente celda si estás ejecutando este ejemplo en un notebook usando el kernel IPython.

# %pip install networkx numpy

1. Definir el problema

Puedes resolver un problema de corte máximo definiendo un problema de grafos 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
)

Output:

Output of the previous code cell

El Solver acepta una cadena como entrada de definición del problema.

# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)

2. Ejecuta el problema

Cuando utilice el método de entrada basado en gráficos, especifique el tipo de problema.

# 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 tu carga de trabajo de Qiskit Function o obtén los resultados de la siguiente manera:

# Print the ID so you can use it later, if necessary
print(maxcut_job.job_id)

# Get job status
print(maxcut_job.status())

Output:

34b53970-d95a-4e24-8763-fc6f3d112843
QUEUED

3. Recuperar el resultado

Recuperar el valor de corte óptimo del diccionario de resultados.

Note

Es posible que haya cambiado la asignación de las variables a la cadena de bits. El diccionario de salida contiene un variables_to_bitstring_index_map subdiccionario, 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}")

Output:

Optimal cut value: 210.0

Puede verificar la exactitud del resultado resolviendo el problema de forma clásica con solucionadores de código abierto como PuLP si el grafo no está densamente conectado. Los problemas de alta densidad pueden requerir solucionadores clásicos avanzados para validar la solución.


Ejemplo: Optimización con restricciones

El ejemplo anterior de «max-cut» es un problema habitual de optimización binaria cuadrática sin restricciones. El solucionador de optimización de Q-CTRL se puede utilizar para diversos tipos de problemas, incluida la optimización con restricciones. Se pueden resolver problemas de cualquier tipo introduciendo la formulación del problema expresada como un polinomio, en el que las restricciones se modelan como términos de penalización.

El siguiente ejemplo muestra cómo construir una función de coste para un problema de optimización con restricciones, la cobertura mínima de vértices (MVC).

Además de los paquetes qiskit-ibm-catalog y qiskit , también utilizará los siguientes paquetes para ejecutar este ejemplo: numpy, networkx, y sympy. Puedes instalar estos paquetes descomentando la siguiente celda si estás ejecutando este ejemplo en un notebook usando el kernel IPython.

# %pip install numpy networkx sympy

1. Definir el problema

Define un problema MVC aleatorio generando un grafo con nodos ponderados aleatoriamente.

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)

Output:

Output of the previous code cell

Un modelo de optimización estándar para MVC ponderado puede formularse como sigue. En primer lugar, hay que añadir una penalización para cualquier caso en que una arista no esté conectada a un vértice del subconjunto. Por lo tanto, ni=1n_i = 1 si el vértice ii está en la cubierta (es decir, en el subconjunto) y ni=0n_i = 0 en caso contrario. En segundo lugar, el objetivo es minimizar el número total de vértices en el subconjunto, que puede representarse mediante la siguiente función:

Minimizey=iVωini\textbf{Minimize}\qquad y = \sum_{i\in V} \omega_i n_i

# 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 punto final de la cubierta, lo que se puede expresar como la desigualdad:

ni+nj1 for all (i,j)En_i + n_j \ge 1 \texttt{ for all } (i,j)\in E

Cualquier caso en el que una arista no esté conectada al vértice de cobertura debe penalizarse. Esto puede representarse en la función de costes añadiendo una penalización de la forma P(1ninj+ninj)P(1-n_i-n_j+n_i n_j) donde PP es una constante de penalización positiva. Por lo tanto, una alternativa no restringida a la desigualdad restringida para MVC ponderado es:

Minimizey=iVωini+P((i,j)E(1ninj+ninj))\textbf{Minimize}\qquad y = \sum_{i\in V}\omega_i n_i + P(\sum_{(i,j)\in E}(1 - n_i - n_j + n_i n_j))

# 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 tu carga de trabajo de Qiskit Function o obtén los resultados de la siguiente manera:

print(mvc_job.status())

Output:

QUEUED

3. Obtenga el resultado

Recuperar la solución y analizar los resultados. Como este problema tiene nodos ponderados, la solución no es simplemente el número mínimo de nodos cubiertos. En su lugar, el coste de la solución representa la suma de los pesos de los vértices incluidos en la cubierta de vértices. Representa el "coste" 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}")

Output:

Solution cost: 10.248198273708624

Obtener soporte

Si tiene alguna pregunta o problema, póngase en contacto con Q-CTRL.


Registro de cambios

  • 11/02/2026: Ahora ofrecemos asistencia para ibm_miami

Próximos pasos

¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.