Resolver el problema de Market Split con el Iskay Quantum Optimizer de Kipu Quantum
Las Qiskit Functions son una funcionalidad experimental disponible únicamente para usuarios del Plan Premium, Plan Flex y 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.
Estimación de uso: 20 segundos en un procesador Heron r2. (NOTA: Esto es solo una estimación. Su tiempo de ejecución puede variar.)
Antecedentes
Este tutorial demuestra cómo resolver el problema de Market Split utilizando el optimizador cuántico Iskay de Kipu Quantum [1]. El problema de Market Split representa un desafío real de asignación de recursos en el que los mercados deben dividirse en regiones de ventas equilibradas para cumplir objetivos exactos de demanda.
El desafío del Market Split
El problema de Market Split presenta un desafío engañosamente simple pero computacionalmente formidable en la asignación de recursos. Considera una empresa con productos que se venden en mercados diferentes, donde cada mercado adquiere un paquete específico de productos (representado por las columnas de la matriz ). El objetivo comercial es dividir estos mercados en dos regiones de ventas equilibradas de modo que cada región reciba exactamente la mitad de la demanda total de cada producto.
Formulación matemática:
Buscamos un vector de asignación binaria , donde:
- asigna el mercado a la Región A
- asigna el mercado a la Región B
- La restricción debe satisfacerse, donde representa las ventas objetivo (típicamente la mitad de la demanda total por producto)
Función de costo:
Para resolver este problema, minimizamos la violación cuadrática de las restricciones:
donde:
- representa las ventas del producto en el mercado
- es la asignación binaria del mercado
- es la meta de ventas para el producto en cada región
- El costo es igual a cero precisamente cuando todas las restricciones se satisfacen
Cada término en la suma representa la desviación cuadrática respecto a la meta de ventas de un producto en particular. Cuando expandimos esta función de costo, obtenemos:
Dado que es una constante, minimizar equivale a minimizar la función cuadrática , que es exactamente un problema QUBO (Quadratic Unconstrained Binary Optimization).
Complejidad computacional:
A pesar de su interpretación comercial directa, este problema exhibe una notable intratabilidad computacional:
- Fallo a pequeña escala: Los solucionadores convencionales de Programación Entera Mixta fallan en instancias con tan solo siete productos bajo un tiempo límite de una hora [4]
- Crecimiento exponencial: El espacio de soluciones crece exponencialmente ( asignaciones posibles), lo que hace inviables los enfoques de fuerza bruta
Esta severa barrera computacional, combinada con su relevancia práctica para la planificación territorial y la asignación de recursos, convierte al problema de Market Split en un punto de referencia ideal para algoritmos de optimización cuántica [4].
¿Qué hace único al enfoque de Iskay?
El optimizador Iskay utiliza el algoritmo bf-DCQO (bias-field digitized counterdiabatic quantum optimization) [1], que representa un avance significativo en la optimización cuántica:
Eficiencia de circuitos: El algoritmo bf-DCQO logra una reducción notable de compuertas [1]:
- Hasta 10 veces menos compuertas de entrelazamiento que el Digital Quantum Annealing (DQA)
- Circuitos significativamente menos profundos que permiten:
- Menor acumulación de errores durante la ejecución cuántica
- Capacidad para abordar problemas más grandes en el hardware cuántico actual
- Sin necesidad de técnicas de mitigación de errores
Diseño no variacional: A diferencia de los algoritmos variacionales que requieren aproximadamente 100 iteraciones, bf-DCQO típicamente necesita solo aproximadamente 10 iteraciones [1]. Esto se logra mediante:
- Cálculos inteligentes del campo de sesgo a partir de distribuciones de estados medidos
- Inicio de cada iteración desde un estado energético cercano a la solución anterior
- Post-procesamiento clásico integrado con búsqueda local
Protocolos contradiabáticos: El algoritmo incorpora términos contradiabáticos que suprimen excitaciones cuánticas no deseadas durante tiempos de evolución cortos, permitiendo que el sistema permanezca cerca del estado fundamental incluso con transiciones rápidas [1].
Requisitos
Antes de iniciar este tutorial, asegúrate de tener instalado lo siguiente:
- Qiskit IBM Runtime (
pip install qiskit-ibm-runtime) - Qiskit Functions (
pip install qiskit-ibm-catalog) - NumPy (
pip install numpy) - Requests (
pip install requests) - Opt Mapper Qiskit addon (
pip install qiskit-addon-opt-mapper)
También necesitarás obtener acceso a la función Iskay Quantum Optimizer del Catálogo de Qiskit Functions.
Configuración
Primero, importa todos los paquetes necesarios para este tutorial.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit-addon-opt-mapper qiskit-ibm-catalog requests
import os
import tempfile
import time
from typing import Tuple, Optional
import numpy as np
import requests
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_addon_opt_mapper import OptimizationProblem
from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo
print("All required libraries imported successfully")
Configurar las credenciales de IBM Quantum
Define tus credenciales de IBM Quantum® Platform. Necesitarás:
- Token de API: Su clave de API de 44 caracteres de IBM Quantum Platform
- CRN de instancia: Su identificador de instancia de IBM Cloud®
token = "<YOUR_API_KEY>"
instance = "<YOUR_INSTANCE_CRN>"
Paso 1: Mapear las entradas clásicas a un problema cuántico
Comenzamos mapeando nuestro problema clásico a una representación compatible con la computación cuántica. Este paso involucra:
- Conexión al Iskay Quantum Optimizer
- Carga y formulación del problema de Market Split
- Comprensión del algoritmo bf-DCQO que lo resolverá
Conexión al Iskay Quantum Optimizer
Comenzamos estableciendo una conexión al Catálogo de Qiskit Functions y cargando el Iskay Quantum Optimizer. El optimizador Iskay es una función cuántica proporcionada por Kipu Quantum que implementa el algoritmo bf-DCQO para resolver problemas de optimización en hardware cuántico.
catalog = QiskitFunctionsCatalog(token=token, instance=instance)
iskay_solver = catalog.load("kipu-quantum/iskay-quantum-optimizer")
print("Iskay optimizer loaded successfully")
print("Ready to solve optimization problems using bf-DCQO algorithm")
Cargar y formular el problema
Comprender el formato de datos del problema
Las instancias de problemas de QOBLIB (Quantum Optimization Benchmarking Library) [2] se almacenan en un formato de texto simple. Examinemos el contenido real de nuestra instancia objetivo ms_03_200_177.dat:
3 20
60 92 161 53 97 2 75 81 6 139 132 45 108 112 181 93 152 200 164 51 1002
176 196 41 143 2 88 0 79 10 71 75 148 82 135 34 187 33 155 58 46 879
68 68 179 173 127 163 48 49 99 78 44 52 173 131 73 198 84 109 180 95 1040
Estructura del formato:
-
Primera línea:
3 203= número de productos (restricciones/filas en la matriz )20= número de mercados (variables/columnas en la matriz )
-
Siguientes 3 líneas: Matriz de coeficientes y vector objetivo
- Cada línea tiene 21 números: los primeros 20 son coeficientes de fila, el último es el objetivo
- Línea 2:
60 92 161 ... 51 | 1002- Los primeros 20 números: Cuánto del Producto 1 vende cada uno de los 20 mercados
- Último número (1002): Ventas objetivo del Producto 1 en una región
- Línea 3:
176 196 41 ... 46 | 879- Ventas del Producto 2 por mercado y objetivo (879)
- Línea 4:
68 68 179 ... 95 | 1040- Ventas del Producto 3 por mercado y objetivo (1040)
Interpretación comercial:
- El Mercado 0 vende: 60 unidades del Producto 1, 176 unidades del Producto 2, 68 unidades del Producto 3
- El Mercado 1 vende: 92 unidades del Producto 1, 196 unidades del Producto 2, 68 unidades del Producto 3
- Y así sucesivamente para los 20 mercados...
- Objetivo: Dividir estos 20 mercados en dos regiones donde cada región reciba exactamente 1002 unidades del Producto 1, 879 unidades del Producto 2 y 1040 unidades del Producto 3
Transformación QUBO
De restricciones a QUBO: la transformación matemática
El poder de la optimización cuántica reside en transformar problemas restringidos en formas cuadráticas sin restricciones [4]. Para el problema de Market Split, convertimos las restricciones de igualdad
donde , en un QUBO penalizando las violaciones de restricciones.
El método de penalización: Dado que necesitamos que se cumpla exactamente, minimizamos la violación cuadrática:
Esto es igual a cero precisamente cuando todas las restricciones se satisfacen. Expandiendo algebraicamente:
Objetivo QUBO: Dado que es constante, nuestra optimización se convierte en:
Información clave: Esta transformación es exacta, no aproximada. Las restricciones de igualdad se elevan al cuadrado de forma natural en forma cuadrática sin requerir variables auxiliares ni parámetros de penalización, lo que hace que esta formulación sea matemáticamente elegante y computacionalmente eficiente para los solucionadores cuánticos [4]. Utilizaremos la clase OptimizationProblem para definir nuestro problema restringido y luego lo convertiremos al formato QUBO usando OptimizationProblemToQubo, ambas del paquete qiskit_addon_opt_mapper. Esto maneja automáticamente la transformación basada en penalización.
Implementar funciones de carga de datos y conversión a QUBO
Ahora definimos tres funciones utilitarias:
parse_marketsplit_dat()- Analiza el formato de archivo.daty extrae las matrices yfetch_marketsplit_data()- Descarga instancias del problema directamente del repositorio QOBLIB
def parse_marketsplit_dat(filename: str) -> Tuple[np.ndarray, np.ndarray]:
"""
Parse a market split problem from a .dat file format.
Parameters
----------
filename : str
Path to the .dat file containing the market split problem data.
Returns
-------
A : np.ndarray
Coefficient matrix of shape (m, n) where m is the number of products
and n is the number of markets.
b : np.ndarray
Target vector of shape (m,) containing the target sales per product.
"""
with open(filename, "r", encoding="utf-8") as f:
lines = [
line.strip()
for line in f
if line.strip() and not line.startswith("#")
]
if not lines:
raise ValueError("Empty or invalid .dat file")
# First line: m n (number of products and markets)
m, n = map(int, lines[0].split())
# Next m lines: each row of A followed by corresponding element of b
A, b = [], []
for i in range(1, m + 1):
values = list(map(int, lines[i].split()))
A.append(values[:-1]) # First n values: product sales per market
b.append(values[-1]) # Last value: target sales for this product
return np.array(A, dtype=np.int32), np.array(b, dtype=np.int32)
def fetch_marketsplit_data(
instance_name: str = "ms_03_200_177.dat",
) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]:
"""
Fetch market split data directly from the QOBLIB repository.
Parameters
----------
instance_name : str
Name of the .dat file to fetch (default: "ms_03_200_177.dat").
Returns
-------
A : np.ndarray or None
Coefficient matrix if successful, None if failed.
b : np.ndarray or None
Target vector if successful, None if failed.
"""
url = f"https://git.zib.de/qopt/qoblib-quantum-optimization-benchmarking-library/-/raw/main/01-marketsplit/instances/{instance_name}"
try:
response = requests.get(url, timeout=30)
response.raise_for_status()
with tempfile.NamedTemporaryFile(
mode="w", suffix=".dat", delete=False, encoding="utf-8"
) as f:
f.write(response.text)
temp_path = f.name
try:
return parse_marketsplit_dat(temp_path)
finally:
os.unlink(temp_path)
except Exception as e:
print(f"Error: {e}")
return None, None
Cargar la instancia del problema
Ahora cargamos la instancia específica del problema ms_03_200_177.dat de QOBLIB [2]. Esta instancia tiene:
- 3 productos (restricciones)
- 20 mercados (variables de decisión binarias)
- Más de 1 millón de asignaciones posibles de mercados por explorar ()
# Load the problem instance
instance_name = "ms_03_200_177.dat"
A, b = fetch_marketsplit_data(instance_name=instance_name)
if A is not None:
print("Successfully loaded problem instance from QOBLIB")
print("\nProblem Instance Analysis:")
print("=" * 50)
print(f"Coefficient Matrix A: {A.shape[0]} × {A.shape[1]}")
print(f" → {A.shape[0]} products (constraints)")
print(f" → {A.shape[1]} markets (decision variables)")
print(f"Target Vector b: {b}")
print(" → Target sales per product for each region")
print(
f"Solution Space: 2^{A.shape[1]} = {2**A.shape[1]:,} possible assignments"
)
Convertir al formato QUBO
Ahora transformamos el problema de optimización con restricciones al formato QUBO:
# Create optimization problem
ms = OptimizationProblem(instance_name.replace(".dat", ""))
# Add binary variables (one for each market)
ms.binary_var_list(A.shape[1])
# Add equality constraints (one for each product)
for idx, rhs in enumerate(b):
ms.linear_constraint(A[idx, :], sense="==", rhs=rhs)
# Convert to QUBO with penalty parameter
qubo = OptimizationProblemToQubo(penalty=1).convert(ms)
print("QUBO Conversion Complete:")
print("=" * 50)
print(f"Number of variables: {qubo.get_num_vars()}")
print(f"Constant term: {qubo.objective.constant}")
print(f"Linear terms: {len(qubo.objective.linear.to_dict())}")
print(f"Quadratic terms: {len(qubo.objective.quadratic.to_dict())}")
Convertir QUBO al formato de Iskay
Ahora necesitamos convertir el objeto QUBO al formato de diccionario requerido por el Iskay Optimizer de Kipu Quantum.
Los argumentos problem y problem_type codifican un problema de optimización de la forma
donde
- Al elegir
problem_type = "binary", especificas que la función de costo está en formatobinary, lo que significa que , es decir, la función de costo está escrita en formulación QUBO/HUBO. - Por otro lado, al elegir
problem_type = "spin", la función de costo está escrita en formulación de Ising, donde .
Los coeficientes del problema deben codificarse en un diccionario de la siguiente manera:
Ten en cuenta que las claves del diccionario deben ser cadenas que contengan una tupla válida de enteros no repetidos. Para problemas binarios, sabemos que:
para (ya que implica ). Entonces, en su formulación QUBO, si tiene tanto contribuciones lineales como contribuciones cuadráticas diagonales , estos términos deben combinarse en un único coeficiente lineal:
Coeficiente lineal total para la variable :
Esto significa:
- Los términos lineales como
"(i, )"contienen: coeficiente lineal original + coeficiente cuadrático diagonal - Los términos cuadráticos diagonales como
"(i, i)"NO deben aparecer en el diccionario final - Solo los términos cuadráticos fuera de la diagonal como
"(i, j)"donde deben incluirse como entradas separadas
Ejemplo: Si su QUBO tiene , el diccionario de Iskay debe contener:
"(0, )":5.0(combinando )"(0, 1)":4.0(término fuera de la diagonal)
NO entradas separadas para "(0, )": 3.0 y "(0, 0)": 2.0.
# Convert QUBO to Iskay dictionary format:
# Create empty Iskay input dictionary
iskay_input_problem = {}
# Convert QUBO to Iskay dictionary format
iskay_input_problem = {"()": qubo.objective.constant}
for i in range(qubo.get_num_vars()):
for j in range(i, qubo.get_num_vars()):
if i == j:
# Add linear term (including diagonal quadratic contribution)
iskay_input_problem[f"({i}, )"] = float(
qubo.objective.linear.to_dict().get(i)
) + float(qubo.objective.quadratic.to_dict().get((i, i)))
else:
# Add off-diagonal quadratic term
iskay_input_problem[f"({i}, {j})"] = float(
qubo.objective.quadratic.to_dict().get((i, j))
)
# Display Iskay dictionary summary
print("Iskay Dictionary Format:")
print("=" * 50)
print(f"Total coefficients: {len(iskay_input_problem)}")
print(f" • Constant term: {iskay_input_problem['()']}")
print(
f" • Linear terms: {sum(1 for k in iskay_input_problem.keys() if k != '()' and ', )' in k)}"
)
print(
f" • Quadratic terms: {sum(1 for k in iskay_input_problem.keys() if k != '()' and ', )' not in k)}"
)
print("\nSample coefficients:")
# Get first 10 and last 5 items properly
items = list(iskay_input_problem.items())
first_10 = list(enumerate(items[:10]))
last_5 = list(enumerate(items[-5:], start=len(items) - 5))
for i, (key, value) in first_10 + last_5:
coeff_type = (
"constant"
if key == "()"
else "linear"
if ", )" in key
else "quadratic"
)
print(f" {key}: {value} ({coeff_type})")
print(" ...")
print("\n✓ Problem ready for Iskay optimizer!")
Comprender el algoritmo bf-DCQO
Antes de ejecutar la optimización, comprendamos el sofisticado algoritmo cuántico que impulsa a Iskay: bf-DCQO (bias-field digitized counterdiabatic quantum optimization) [1].