{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "title",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Resuelva el problema de la división del mercado con el optimizador cuántico Iskay de Kipu Quantum\"\n",
        "description: \"Aprenda a resolver el problema de la división del mercado utilizando el optimizador cuántico Iskay con el algoritmo bf-DCQO en un hardware de IBM Quantum\"\n",
        "---\n",
        "\n",
        "<span id=\"solve-the-market-split-problem-with-kipu-quantums-iskay-quantum-optimizer\" />\n",
        "\n",
        "# Resuelva el problema de la división del mercado con el optimizador cuántico Iskay de Kipu Quantum\n",
        "\n",
        "{/* cspell:ignore adiabaticity, HUBO, bitflip, metaheuristic, fontweight, fontsize, QOBLIB, Zuse, Kochenberger, Tramontani, Weninger, edgecolor, nonumber */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "note",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Nota\">\n",
        "  Qiskit Functions son una función experimental disponible únicamente para los usuarios de los planes IBM Quantum® Premium Plan, Flex y On-Prem (a través de la API IBM Quantum Platform ). Se trata de versiones preliminares sujetas a cambios.\n",
        "</Admonition>\n",
        "\n",
        "*Estimación de uso: 20 segundos en un procesador Heron r2. (NOTA: Esto es sólo una estimación. Su tiempo de ejecución puede variar)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "background",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## En segundo plano\n",
        "\n",
        "Este tutorial muestra cómo resolver el problema Market Split utilizando [el optimizador cuántico Iskay de Kipu Quantum](/docs/guides/kipu-optimization) [\\[1\\]](#references). El problema de la división del mercado representa un reto de asignación de recursos del mundo real en el que los mercados deben dividirse en regiones de ventas equilibradas para satisfacer los objetivos exactos de demanda.\n",
        "\n",
        "<span id=\"the-market-split-challenge\" />\n",
        "\n",
        "### El reto de la división del mercado\n",
        "\n",
        "El problema de la división del mercado plantea un reto aparentemente sencillo, pero formidable desde el punto de vista computacional, en la asignación de recursos. Consideremos una empresa con $m$ productos que se venden en $n$ mercados diferentes, donde cada mercado compra un paquete específico de productos (representado por las columnas de la matriz $A$ ). El objetivo de la empresa es dividir estos mercados en dos regiones de venta equilibradas, de forma que cada región reciba exactamente la mitad de la demanda total de cada producto.\n",
        "\n",
        "**Formulación matemática:**\n",
        "\n",
        "Buscamos un vector de asignación binario $x$, donde:\n",
        "\n",
        "* $x_j = 1$ asigna el mercado $j$ a la Región A\n",
        "* $x_j = 0$ asigna el mercado $j$ a la Región B\n",
        "* Debe cumplirse la restricción $Ax = b$, donde $b$ representa el objetivo de ventas (normalmente la mitad de la demanda total por producto)\n",
        "\n",
        "**Función de costes:**\n",
        "\n",
        "Para resolver este problema, minimizamos la violación de la restricción al cuadrado:\n",
        "\n",
        "$C(x) = ||Ax - b||^2 = \\sum_{i=1}^{m} \\left(\\sum_{j=1}^{n} A_{ij}x_j - b_i\\right)^2$\n",
        "\n",
        "donde:\n",
        "\n",
        "* $A_{ij}$ representa las ventas del producto $i$ en el mercado $j$\n",
        "* $x_j \\in \\{0,1\\}$ es la asignación binaria del mercado $j$\n",
        "* $b_i$ es el objetivo de ventas del producto $i$ en cada región\n",
        "* El coste es igual a cero precisamente cuando se cumplen todas las restricciones\n",
        "\n",
        "Cada término de la suma representa la desviación al cuadrado de las ventas objetivo de un producto concreto. Al expandir esta función de costes, obtenemos:\n",
        "\n",
        "$C(x) = x^T A^T A x - 2b^T A x + b^T b$\n",
        "\n",
        "Dado que $b^T b$ es una constante, minimizar $C(x)$ equivale a minimizar la función cuadrática $x^T A^T A x - 2b^T A x$, que es exactamente un problema QUBO (Quadratic Unconstrained Binary Optimization).\n",
        "\n",
        "**Complejidad computacional:**\n",
        "\n",
        "A pesar de su sencilla interpretación comercial, este problema presenta una notable intratabilidad computacional:\n",
        "\n",
        "* **Fallos a pequeña escala** : Los solucionadores convencionales de programación entera mixta fallan en instancias con tan solo siete productos bajo un tiempo de espera de una hora [\\[4\\]](#references)\n",
        "* **Crecimiento exponencial** : El espacio de soluciones crece exponencialmente ( $2^n$ posibles asignaciones), lo que hace inviables los enfoques de fuerza bruta\n",
        "\n",
        "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 la división del mercado en una referencia ideal para los algoritmos de optimización cuántica [\\[4\\]](#references).\n",
        "\n",
        "<span id=\"what-makes-iskays-approach-unique\" />\n",
        "\n",
        "### ¿Qué hace que el enfoque de Iskay sea único?\n",
        "\n",
        "El optimizador Iskay utiliza el algoritmo **bf-DCQO (bias-field digitized counterdiabatic quantum optimization)** [\\[1\\]](#references), que representa un avance significativo en la optimización cuántica:\n",
        "\n",
        "**Eficiencia del circuito** : El algoritmo bf-DCQO consigue una notable reducción de compuertas [\\[1\\]](#references) :\n",
        "\n",
        "* Hasta **10 veces menos puertas de enredo** que el Recocido Cuántico Digital (DQA)\n",
        "* Permiten circuitos significativamente menos profundos:\n",
        "  * Menor acumulación de errores durante la ejecución cuántica\n",
        "  * Capacidad para abordar problemas de mayor envergadura en el hardware cuántico actual\n",
        "  * No se necesitan técnicas de mitigación de errores\n",
        "\n",
        "**Diseño no variacional** : A diferencia de los algoritmos variacionales que requieren aproximadamente 100 iteraciones, bf-DCQO normalmente sólo necesita **aproximadamente 10 iteraciones** [\\[1\\]](#references). Esto se consigue mediante:\n",
        "\n",
        "* Cálculos inteligentes del campo de polarización a partir de distribuciones de estado medidas\n",
        "* Partiendo en cada iteración de un estado energético próximo a la solución anterior\n",
        "* Postprocesado clásico integrado con búsqueda local\n",
        "\n",
        "**Protocolos contradiabáticos** : El algoritmo incorpora términos contradiabáticos que suprimen las excitaciones cuánticas no deseadas durante tiempos de evolución cortos, lo que permite al sistema permanecer cerca del estado fundamental incluso con transiciones rápidas [\\[1\\]](#references).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "requirements",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de empezar este tutorial, asegúrate de que tienes instalado lo siguiente:\n",
        "\n",
        "* Qiskit IBM Tiempo de ejecución (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions (`pip install qiskit-ibm-catalog`)\n",
        "* NumPy (`pip install numpy`)\n",
        "* Solicitudes (`pip install requests`)\n",
        "* Complemento Opt Mapper Qiskit (`pip install qiskit-addon-opt-mapper`)\n",
        "\n",
        "También tendrá que acceder a la [función Iskay Quantum Optimizer](/functions?id=kipu-quantum-iskay-quantum-optimizer) desde Qiskit Functions Catalog.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "setup",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuración\n",
        "\n",
        "En primer lugar, importe todos los paquetes necesarios para este tutorial.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "imports",
      "metadata": {},
      "outputs": [],
      "source": [
        "import os\n",
        "import tempfile\n",
        "import time\n",
        "from typing import Tuple, Optional\n",
        "\n",
        "import numpy as np\n",
        "import requests\n",
        "\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "\n",
        "from qiskit_addon_opt_mapper import OptimizationProblem\n",
        "from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo\n",
        "\n",
        "print(\"All required libraries imported successfully\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "credentials",
      "metadata": {},
      "source": [
        "<span id=\"configure-ibm-quantum-credentials\" />\n",
        "\n",
        "### Configurar las credenciales de IBM Quantum\n",
        "\n",
        "Defina sus [IBM Quantum® Platform](/) credenciales. Necesitará:\n",
        "\n",
        "* **Token API** : Su clave API de 44 caracteres de IBM Quantum Platform\n",
        "* **CRN de instancia** : Su identificador de instancia IBM Cloud®\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "creds",
      "metadata": {},
      "outputs": [],
      "source": [
        "token = \"<YOUR_API_KEY>\"\n",
        "instance = \"<YOUR_INSTANCE_CRN>\""
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step1",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## Paso 1: Asignar entradas clásicas a un problema cuántico\n",
        "\n",
        "Empezaremos trasladando nuestro problema clásico a una representación compatible con la cuántica. Este paso implica:\n",
        "\n",
        "1. Conexión al optimizador cuántico Iskay\n",
        "2. Carga y formulación del problema de Market Split\n",
        "3. Comprender el algoritmo bf-DCQO que lo resolverá\n",
        "\n",
        "<span id=\"connect-to-iskay-quantum-optimizer\" />\n",
        "\n",
        "### Conéctese a Iskay Quantum Optimizer\n",
        "\n",
        "Empezaremos estableciendo una conexión con Qiskit Functions Catalog y cargando el optimizador cuántico Iskay. 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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "load_solver",
      "metadata": {},
      "outputs": [],
      "source": [
        "catalog = QiskitFunctionsCatalog(token=token, instance=instance)\n",
        "iskay_solver = catalog.load(\"kipu-quantum/iskay-quantum-optimizer\")\n",
        "\n",
        "print(\"Iskay optimizer loaded successfully\")\n",
        "print(\"Ready to solve optimization problems using bf-DCQO algorithm\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step2",
      "metadata": {},
      "source": [
        "<span id=\"load-and-formulate-the-problem\" />\n",
        "\n",
        "### Cargar y formular el problema\n",
        "\n",
        "<span id=\"understand-the-problem-data-format\" />\n",
        "\n",
        "#### Comprender el formato de los datos del problema\n",
        "\n",
        "Las instancias de problemas de QOBLIB (Quantum Optimization Benchmarking Library) [\\[2\\]](#references) se almacenan en un formato de texto simple. Examinemos el contenido real de nuestra instancia de destino `ms_03_200_177.dat`:\n",
        "\n",
        "```text\n",
        "3 20\n",
        "60   92  161   53   97    2   75   81    6  139  132   45  108  112  181   93  152  200  164   51 1002\n",
        "176  196   41  143    2   88    0   79   10   71   75  148   82  135   34  187   33  155   58   46  879\n",
        "68   68  179  173  127  163   48   49   99   78   44   52  173  131   73  198   84  109  180   95 1040\n",
        "```\n",
        "\n",
        "**Estructura del formato:**\n",
        "\n",
        "* **Primera línea:** `3 20`\n",
        "  * `3` = número de productos (restricciones/filas de la matriz $A$ )\n",
        "  * `20` = número de mercados (variables/columnas de la matriz $A$ )\n",
        "\n",
        "* **3 líneas siguientes:** Matriz de coeficientes $A$ y vector objetivo $b$\n",
        "  * Cada línea tiene 21 números: los 20 primeros son coeficientes de fila, el último es el objetivo\n",
        "  * Línea 2: `60 92 161 ... 51 | 1002`\n",
        "    * Los 20 primeros números: Cuánto vende del Producto 1 cada uno de los 20 mercados\n",
        "    * Último número (1002): Objetivo de ventas para el Producto 1 en una región\n",
        "  * Línea 3: `176 196 41 ... 46 | 879`\n",
        "    * Producto 2 ventas por mercado y objetivo (879)\n",
        "  * Línea 4: `68 68 179 ... 95 | 1040`\n",
        "    * Producto 3 ventas por mercado y objetivo (1040)\n",
        "\n",
        "**Interpretación comercial:**\n",
        "\n",
        "* El mercado 0 vende: 60 unidades del producto 1, 176 unidades del producto 2, 68 unidades del producto 3\n",
        "* Mercado 1 vende: 92 unidades del Producto 1, 196 unidades del Producto 2, 68 unidades del Producto 3\n",
        "* Y así para los 20 mercados...\n",
        "* **Objetivo** : Dividir estos 20 mercados en dos regiones en las que cada región reciba exactamente 1002 unidades del Producto 1, 879 unidades del Producto 2 y 1040 unidades del Producto 3\n",
        "\n",
        "<span id=\"qubo-transformation\" />\n",
        "\n",
        "#### Transformación QUBO\n",
        "\n",
        "<span id=\"from-constraints-to-qubo-the-mathematical-transformation\" />\n",
        "\n",
        "## De las restricciones al QUBO: la transformación matemática\n",
        "\n",
        "El poder de la optimización cuántica reside en transformar problemas con restricciones en formas cuadráticas sin restricciones [\\[4\\]](#references). Para el problema de Market Split, convertimos las restricciones de igualdad\n",
        "\n",
        "$Ax = b$\n",
        "\n",
        "donde $x ∈ \\{0,1\\}^n$, en un QUBO penalizando las violaciones de las restricciones.\n",
        "\n",
        "**El método de penalización:** Como necesitamos que $Ax = b$ se cumpla exactamente, minimizamos la violación al cuadrado: $f(x) = ||Ax - b||^2$\n",
        "\n",
        "Es igual a cero precisamente cuando se cumplen todas las restricciones. Expandir algebraicamente: $f(x) = (Ax - b)^T(Ax - b) = x^T A^T A x - 2b^T A x + b^T b$\n",
        "\n",
        "**Objetivo QUBO:** Dado que $b^T b$ es constante, nuestra optimización se convierte en: $\\text{minimize} \\quad Q(x) = x^T(A^T A)x - 2(A^T b)^T x$\n",
        "\n",
        "**Una idea clave:** Esta transformación es exacta, no aproximada. Las restricciones de igualdad se cuadran naturalmente 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\\]](#references). Usaremos la clase `OptimizationProblem` para definir nuestro problema restringido, y luego lo convertiremos a formato QUBO usando `OptimizationProblemToQubo`, ambos del paquete **qiskit\\_addon\\_opt\\_mapper**. Esto gestiona automáticamente la transformación basada en penalizaciones.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "functions_intro",
      "metadata": {},
      "source": [
        "<span id=\"implement-data-loading-and-qubo-conversion-functions\" />\n",
        "\n",
        "### Implementar funciones de carga de datos y conversión QUBO\n",
        "\n",
        "A continuación definimos tres funciones de utilidad:\n",
        "\n",
        "1. `parse_marketsplit_dat()` - Analiza el formato de archivo `.dat` y extrae las matrices $A$ y $b$\n",
        "2. `fetch_marketsplit_data()` - Descarga instancias de problemas directamente del repositorio QOBLIB\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "functions",
      "metadata": {},
      "outputs": [],
      "source": [
        "def parse_marketsplit_dat(filename: str) -> Tuple[np.ndarray, np.ndarray]:\n",
        "    \"\"\"\n",
        "    Parse a market split problem from a .dat file format.\n",
        "\n",
        "    Parameters\n",
        "    ----------\n",
        "    filename : str\n",
        "        Path to the .dat file containing the market split problem data.\n",
        "\n",
        "    Returns\n",
        "    -------\n",
        "    A : np.ndarray\n",
        "        Coefficient matrix of shape (m, n) where m is the number of products\n",
        "        and n is the number of markets.\n",
        "    b : np.ndarray\n",
        "        Target vector of shape (m,) containing the target sales per product.\n",
        "    \"\"\"\n",
        "    with open(filename, \"r\", encoding=\"utf-8\") as f:\n",
        "        lines = [\n",
        "            line.strip()\n",
        "            for line in f\n",
        "            if line.strip() and not line.startswith(\"#\")\n",
        "        ]\n",
        "\n",
        "    if not lines:\n",
        "        raise ValueError(\"Empty or invalid .dat file\")\n",
        "\n",
        "    # First line: m n (number of products and markets)\n",
        "    m, n = map(int, lines[0].split())\n",
        "\n",
        "    # Next m lines: each row of A followed by corresponding element of b\n",
        "    A, b = [], []\n",
        "    for i in range(1, m + 1):\n",
        "        values = list(map(int, lines[i].split()))\n",
        "        A.append(values[:-1])  # First n values: product sales per market\n",
        "        b.append(values[-1])  # Last value: target sales for this product\n",
        "\n",
        "    return np.array(A, dtype=np.int32), np.array(b, dtype=np.int32)\n",
        "\n",
        "\n",
        "def fetch_marketsplit_data(\n",
        "    instance_name: str = \"ms_03_200_177.dat\",\n",
        ") -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]:\n",
        "    \"\"\"\n",
        "    Fetch market split data directly from the QOBLIB repository.\n",
        "\n",
        "    Parameters\n",
        "    ----------\n",
        "    instance_name : str\n",
        "        Name of the .dat file to fetch (default: \"ms_03_200_177.dat\").\n",
        "\n",
        "    Returns\n",
        "    -------\n",
        "    A : np.ndarray or None\n",
        "        Coefficient matrix if successful, None if failed.\n",
        "    b : np.ndarray or None\n",
        "        Target vector if successful, None if failed.\n",
        "    \"\"\"\n",
        "    url = f\"https://git.zib.de/qopt/qoblib-quantum-optimization-benchmarking-library/-/raw/main/01-marketsplit/instances/{instance_name}\"\n",
        "\n",
        "    try:\n",
        "        response = requests.get(url, timeout=30)\n",
        "        response.raise_for_status()\n",
        "\n",
        "        with tempfile.NamedTemporaryFile(\n",
        "            mode=\"w\", suffix=\".dat\", delete=False, encoding=\"utf-8\"\n",
        "        ) as f:\n",
        "            f.write(response.text)\n",
        "            temp_path = f.name\n",
        "\n",
        "        try:\n",
        "            return parse_marketsplit_dat(temp_path)\n",
        "        finally:\n",
        "            os.unlink(temp_path)\n",
        "    except Exception as e:\n",
        "        print(f\"Error: {e}\")\n",
        "        return None, None"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "load_intro",
      "metadata": {},
      "source": [
        "<span id=\"load-the-problem-instance\" />\n",
        "\n",
        "### Cargar la instancia del problema\n",
        "\n",
        "Ahora cargamos la instancia específica del problema `ms_03_200_177.dat` desde QOBLIB \\[2]. Esta instancia tiene:\n",
        "\n",
        "* 3 productos (limitaciones)\n",
        "* 20 mercados (variables de decisión binarias)\n",
        "* Más de 1 millón de posibles asignaciones de mercado para explorar ( $2^{20} = 1,048,576$ )\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "load",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Load the problem instance\n",
        "instance_name = \"ms_03_200_177.dat\"\n",
        "A, b = fetch_marketsplit_data(instance_name=instance_name)\n",
        "\n",
        "if A is not None:\n",
        "    print(\"Successfully loaded problem instance from QOBLIB\")\n",
        "    print(\"\\nProblem Instance Analysis:\")\n",
        "    print(\"=\" * 50)\n",
        "    print(f\"Coefficient Matrix A: {A.shape[0]} × {A.shape[1]}\")\n",
        "    print(f\"   → {A.shape[0]} products (constraints)\")\n",
        "    print(f\"   → {A.shape[1]} markets (decision variables)\")\n",
        "    print(f\"Target Vector b: {b}\")\n",
        "    print(\"   → Target sales per product for each region\")\n",
        "    print(\n",
        "        f\"Solution Space: \"\n",
        "        f\"2^{A.shape[1]} = {2**A.shape[1]:,} possible assignments\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "convert_intro",
      "metadata": {},
      "source": [
        "<span id=\"convert-to-qubo-format\" />\n",
        "\n",
        "### Convertir al formato QUBO\n",
        "\n",
        "Ahora transformamos el problema de optimización restringido en formato QUBO:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "convert",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create optimization problem\n",
        "ms = OptimizationProblem(instance_name.replace(\".dat\", \"\"))\n",
        "\n",
        "# Add binary variables (one for each market)\n",
        "ms.binary_var_list(A.shape[1])\n",
        "\n",
        "# Add equality constraints (one for each product)\n",
        "for idx, rhs in enumerate(b):\n",
        "    ms.linear_constraint(A[idx, :], sense=\"==\", rhs=rhs)\n",
        "\n",
        "# Convert to QUBO with penalty parameter\n",
        "qubo = OptimizationProblemToQubo(penalty=1).convert(ms)\n",
        "\n",
        "print(\"QUBO Conversion Complete:\")\n",
        "print(\"=\" * 50)\n",
        "print(f\"Number of variables: {qubo.get_num_vars()}\")\n",
        "print(f\"Constant term: {qubo.objective.constant}\")\n",
        "print(f\"Linear terms: {len(qubo.objective.linear.to_dict())}\")\n",
        "print(f\"Quadratic terms: {len(qubo.objective.quadratic.to_dict())}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1d307072",
      "metadata": {},
      "source": [
        "<span id=\"convert-qubo-to-iskay-format\" />\n",
        "\n",
        "### Convertir QUBO a formato Iskay\n",
        "\n",
        "Ahora necesitamos convertir el objeto QUBO al formato de diccionario requerido por el Optimizador Iskay de Kipu Quantum.\n",
        "\n",
        "Los argumentos `problem` y `problem_type` codifican un problema de optimización de la forma\n",
        "\n",
        "$$\n",
        "\\begin{align}\n",
        "\\min_{(x_1, x_2, \\ldots, x_n) \\in D} C(x_1, x_2, \\ldots, x_n) \\nonumber\n",
        "\\end{align}\n",
        "$$\n",
        "\n",
        "donde\n",
        "\n",
        "$$\n",
        "C(x_1, ... , x_n) = a + \\sum_{i} b_i x_i + \\sum_{i, j} c_{i, j} x_i x_j + ... + \\sum_{k_1, ..., k_m} g_{k_1, ..., k_m} x_{k_1} ... x_{k_m}\n",
        "$$\n",
        "\n",
        "* Al elegir `problem_type = \"binary\"`, se especifica que la función de coste está en formato `binary` , lo que significa que $D = \\{0,  1\\}^{n}$, es decir, la función de coste está escrita en formulación QUBO/HUBO.\n",
        "* Por otra parte, eligiendo `problem_type = \"spin\"`, la función de coste se escribe en la formulación de Ising, donde $D = \\{-1, 1\\}^{n}$.\n",
        "\n",
        "Los coeficientes del problema deben codificarse en un diccionario de la siguiente manera:\n",
        "\n",
        "$$\n",
        "\\begin{align} \\nonumber\n",
        "&\\texttt{\\{} \\\\ \\nonumber\n",
        "&\\texttt{\"()\"}&: \\quad &a, \\\\ \\nonumber\n",
        "&\\texttt{\"(i,)\"}&: \\quad &b_i, \\\\ \\nonumber\n",
        "&\\texttt{\"(i, j)\"}&: \\quad &c_{i, j}, \\quad (i \\neq j) \\\\ \\nonumber\n",
        "&\\quad  \\vdots \\\\ \\nonumber\n",
        "&\\texttt{\"(} k_1, ..., k_m  \\texttt{)\"}&: \\quad &g_{k_1, ..., k_m}, \\quad (k_1 \\neq k_2 \\neq \\dots \\neq k_m) \\\\ \\nonumber\n",
        "&\\texttt{\\}}\n",
        "\\end{align}\n",
        "$$\n",
        "\n",
        "Tenga en cuenta que las claves del diccionario deben ser cadenas que contengan una tupla válida de números enteros no repetidos. Para los problemas binarios, sabemos que:\n",
        "\n",
        "$$\n",
        "x_i^2 = x_i\n",
        "$$\n",
        "\n",
        "para $i=j$ (ya que $x_i \\in \\{0,1\\}$ significa $x_i \\cdot x_i = x_i$ ). Por tanto, en su formulación QUBO, si tiene tanto contribuciones lineales $b_i x_i$ como contribuciones cuadráticas diagonales $c_{i,i} x_i^2$, estos términos deben combinarse en un único coeficiente lineal:\n",
        "\n",
        "**Coeficiente lineal total para la variable $x_i$** : $b_i + c_{i,i}$\n",
        "\n",
        "Esto significa:\n",
        "\n",
        "* Los términos lineales como `\"(i, )\"` contienen: coeficiente lineal original + coeficiente cuadrático diagonal\n",
        "* Los términos cuadráticos diagonales como `\"(i, i)\"` **NO** deben aparecer en el diccionario final\n",
        "* Sólo los términos cuadráticos no diagonales como `\"(i, j)\"` donde $i \\neq j$ deben incluirse como entradas separadas\n",
        "\n",
        "**Ejemplo:** Si su QUBO tiene $3x_1 + 2x_1^2 + 4x_1 x_2$, el diccionario Iskay debe contener:\n",
        "\n",
        "* `\"(0, )\"`: `5.0` (combinando $3 + 2 = 5$ )\n",
        "* `\"(0, 1)\"`: `4.0` (término no diagonal)\n",
        "\n",
        "**NO** hay entradas separadas para `\"(0, )\"`: `3.0` y `\"(0, 0)\"`: `2.0`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "57eda6fd",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Convert QUBO to Iskay dictionary format:\n",
        "\n",
        "# Create empty Iskay input dictionary\n",
        "iskay_input_problem = {}\n",
        "\n",
        "# Convert QUBO to Iskay dictionary format\n",
        "iskay_input_problem = {\"()\": qubo.objective.constant}\n",
        "\n",
        "for i in range(qubo.get_num_vars()):\n",
        "    for j in range(i, qubo.get_num_vars()):\n",
        "        if i == j:\n",
        "            # Add linear term (including diagonal quadratic contribution)\n",
        "            iskay_input_problem[f\"({i}, )\"] = float(\n",
        "                qubo.objective.linear.to_dict().get(i)\n",
        "            ) + float(qubo.objective.quadratic.to_dict().get((i, i)))\n",
        "        else:\n",
        "            # Add off-diagonal quadratic term\n",
        "            iskay_input_problem[f\"({i}, {j})\"] = float(\n",
        "                qubo.objective.quadratic.to_dict().get((i, j))\n",
        "            )\n",
        "\n",
        "# Display Iskay dictionary summary\n",
        "print(\"Iskay Dictionary Format:\")\n",
        "print(\"=\" * 50)\n",
        "print(f\"Total coefficients: {len(iskay_input_problem)}\")\n",
        "print(f\"  • Constant term: {iskay_input_problem['()']}\")\n",
        "print(\n",
        "    f\"  • Linear terms: \"\n",
        "    f\"{sum(1 for k in iskay_input_problem.keys() if k != '()' and ', )' in k)}\"\n",
        ")\n",
        "print(\n",
        "    f\"  • Quadratic terms: \"\n",
        "    f\"{sum(1 for k in iskay_input_problem.keys() if k != '()' and ', )' not in k)}\"\n",
        ")\n",
        "print(\"\\nSample coefficients:\")\n",
        "\n",
        "# Get first 10 and last 5 items properly\n",
        "items = list(iskay_input_problem.items())\n",
        "first_10 = list(enumerate(items[:10]))\n",
        "last_5 = list(enumerate(items[-5:], start=len(items) - 5))\n",
        "\n",
        "for i, (key, value) in first_10 + last_5:\n",
        "    coeff_type = (\n",
        "        \"constant\"\n",
        "        if key == \"()\"\n",
        "        else \"linear\"\n",
        "        if \", )\" in key\n",
        "        else \"quadratic\"\n",
        "    )\n",
        "    print(f\"  {key}: {value} ({coeff_type})\")\n",
        "print(\"  ...\")\n",
        "print(\"\\n✓ Problem ready for Iskay optimizer!\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step3",
      "metadata": {},
      "source": [
        "<span id=\"understand-the-bf-dcqo-algorithm\" />\n",
        "\n",
        "### Comprender el algoritmo bf-DCQO\n",
        "\n",
        "Antes de ejecutar la optimización, vamos a entender el sofisticado algoritmo cuántico que impulsa Iskay: **bf-DCQO (bias-field digitized counterdiabatic quantum optimization)** [\\[1\\]](#references).\n",
        "\n",
        "<span id=\"what-is-bf-dcqo\" />\n",
        "\n",
        "#### ¿Qué es bf-DCQO?\n",
        "\n",
        "bf-DCQO se basa en la evolución temporal de un sistema cuántico en el que la solución del problema está codificada en el **estado fundamental** (estado de menor energía) del Hamiltoniano cuántico final [\\[1\\]](#references). El algoritmo aborda un reto fundamental en la optimización cuántica:\n",
        "\n",
        "**El reto** : La computación cuántica adiabática tradicional requiere una evolución muy lenta para mantener las condiciones del estado básico según el teorema adiabático. Esto exige circuitos cuánticos cada vez más profundos a medida que aumenta la complejidad del problema, lo que conlleva más operaciones de compuerta y errores acumulados.\n",
        "\n",
        "**La solución** : bf-DCQO utiliza protocolos contradiabáticos para permitir una evolución rápida manteniendo la fidelidad al estado base, lo que reduce drásticamente la profundidad del circuito.\n",
        "\n",
        "<span id=\"mathematical-framework\" />\n",
        "\n",
        "#### Marco matemático\n",
        "\n",
        "El algoritmo minimiza una función de coste de la forma:\n",
        "\n",
        "$\\min_{(x_1,x_2,...,x_n) \\in D} C(x_1,x_2,...,x_n)$\n",
        "\n",
        "donde $D = \\{0,1\\}^n$ para variables binarias y:\n",
        "\n",
        "$C(x) = a + \\sum_i b_i x_i + \\sum_{i,j} c_{ij} x_i x_j + ... + \\sum g_{k_1,...,k_m} x_{k_1}...x_{k_m}$\n",
        "\n",
        "Para nuestro problema de Market Split, la función de costes es:\n",
        "\n",
        "$C(x) = ||Ax - b||^2 = x^T A^T A x - 2 b^T A x + b^T b$\n",
        "\n",
        "<span id=\"the-role-of-counterdiabatic-terms\" />\n",
        "\n",
        "#### El papel de los términos antidiabéticos\n",
        "\n",
        "**Los términos contradiabáticos** son términos adicionales introducidos en el Hamiltoniano dependiente del tiempo que suprimen las excitaciones no deseadas durante la evolución cuántica. He aquí por qué son cruciales:\n",
        "\n",
        "En la optimización cuántica adiabática, hacemos evolucionar el sistema según un Hamiltoniano dependiente del tiempo:\n",
        "\n",
        "$H(t) = \\left(1 - \\frac{t}{T}\\right) H_{\\text{initial}} + \\frac{t}{T} H_{\\text{problem}}$\n",
        "\n",
        "donde $H_{\\text{problem}}$ codifica nuestro problema de optimización. Para mantener el estado básico durante la evolución rápida, añadimos términos contradiabáticos:\n",
        "\n",
        "$H_{\\text{CD}}(t) = H(t) + H_{\\text{counter}}(t)$\n",
        "\n",
        "Estos términos contradiabáticos hacen lo siguiente:\n",
        "\n",
        "1. **Suprimir las transiciones no deseadas** : Evitar que el estado cuántico salte a estados excitados durante la evolución rápida\n",
        "2. **Permiten tiempos de evolución más cortos** : Permiten alcanzar el estado final mucho más rápido sin violar la adiabaticidad\n",
        "3. **Reducir la profundidad del circuito** : Una evolución más corta conlleva menos puertas y menos errores\n",
        "\n",
        "El impacto práctico es espectacular: bf-DCQO utiliza hasta **10 veces menos puertas de enredo** que Digital Quantum Annealing [\\[1\\]](#references), lo que lo hace práctico para el ruidoso hardware cuántico actual.\n",
        "\n",
        "<span id=\"bias-field-iterative-optimization\" />\n",
        "\n",
        "#### Optimización iterativa del campo de sesgo\n",
        "\n",
        "A diferencia de los algoritmos variacionales que optimizan los parámetros del circuito a través de muchas iteraciones, bf-DCQO utiliza un **enfoque guiado por el campo de polarización** que converge en aproximadamente 10 iteraciones \\[1] :\n",
        "\n",
        "**Proceso de iteración:**\n",
        "\n",
        "1. **Evolución cuántica inicial** : Comenzar con un circuito cuántico que implemente el protocolo de evolución contradiabática\n",
        "\n",
        "2. **Medición** : Medir el estado cuántico para obtener una distribución de probabilidad sobre cadenas de bits\n",
        "\n",
        "3. **Cálculo del campo de polarización** : Analiza las estadísticas de medición y calcula un campo de polarización óptimo $h_i$ para cada qubit: $h_i = \\text{f}(\\text{measurement statistics}, \\text{previous solutions})$\n",
        "\n",
        "4. **Siguiente iteración** : El campo de polarización modifica el Hamiltoniano para la siguiente iteración: $H_{\\text{next}} = H_{\\text{problem}} + \\sum_i h_i \\sigma_i^z$\n",
        "\n",
        "   Esto permite empezar cerca de la buena solución encontrada anteriormente, realizando de hecho una forma de \"búsqueda local cuántica\"\n",
        "\n",
        "5. **Convergencia** : Repetir hasta que la calidad de la solución se estabilice o se alcance un número máximo de iteraciones\n",
        "\n",
        "**Ventaja** clave: Cada iteración proporciona un progreso significativo hacia la solución óptima al incorporar información de mediciones anteriores, a diferencia de los métodos variacionales que deben explorar el espacio de parámetros a ciegas.\n",
        "\n",
        "<span id=\"integrated-classical-post-processing\" />\n",
        "\n",
        "#### Postprocesamiento clásico integrado\n",
        "\n",
        "Tras la convergencia de la optimización cuántica, Iskay realiza un postprocesamiento clásico de **búsqueda local** :\n",
        "\n",
        "* **Exploración mediante inversión de bits** : Invertir sistemática o aleatoriamente los bits en la mejor solución medida.\n",
        "* **Evaluación energética** : Calcular $C(x)$ para cada solución modificada\n",
        "* **Selección codiciosa** : Aceptar mejoras que reduzcan la función de coste\n",
        "* **Múltiples pasadas** : Realiza varias pasadas (controladas por `postprocessing_level`)\n",
        "\n",
        "Este enfoque híbrido compensa los errores de cambio de bits derivados de las imperfecciones del hardware y los errores de lectura, garantizando soluciones de alta calidad incluso en dispositivos cuánticos ruidosos.\n",
        "\n",
        "<span id=\"why-bf-dcqo-excels-on-current-hardware\" />\n",
        "\n",
        "#### Por qué bf-DCQO destaca en el hardware actual\n",
        "\n",
        "El algoritmo bf-DCQO está diseñado específicamente para sobresalir en los dispositivos cuánticos ruidosos de escala intermedia (NISQ) actuales [\\[1\\]](#references) :\n",
        "\n",
        "1. **Resistencia a los errores** : Menos compuertas (10 veces menos) significa menos acumulación de errores\n",
        "2. **No se requiere mitigación de errores** : La eficiencia inherente del algoritmo elimina la necesidad de costosas técnicas de mitigación de errores [\\[1\\]](#references)\n",
        "3. **Escalabilidad** : Puede manejar problemas de hasta 156 qubits (156 variables binarias) con mapeo directo de qubits [\\[1\\]](#references)\n",
        "4. **Rendimiento demostrado** : Alcanza ratios de aproximación del 100% en las instancias de referencia MaxCut y HUBO [\\[1\\]](#references)\n",
        "\n",
        "Veamos ahora este potente algoritmo en acción en nuestro problema de Market Split\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step4",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## Paso 2: Optimizar el problema para la ejecución en hardware cuántico\n",
        "\n",
        "El algoritmo bf-DCQO se encarga automáticamente de la optimización de los circuitos, creando circuitos cuánticos poco profundos con términos contradiabáticos diseñados específicamente para el backend objetivo.\n",
        "\n",
        "<span id=\"configure-the-optimization\" />\n",
        "\n",
        "### Configurar la optimización\n",
        "\n",
        "El Optimizador Iskay requiere varios parámetros clave para resolver eficazmente su problema de optimización. Examinemos cada parámetro y su papel en el proceso de optimización cuántica:\n",
        "\n",
        "<span id=\"required-parameters\" />\n",
        "\n",
        "#### Parámetros necesarios\n",
        "\n",
        "| Parámetro          | Tipo               | Descripción                                                            | Ejemplo                                     |\n",
        "| ------------------ | ------------------ | ---------------------------------------------------------------------- | ------------------------------------------- |\n",
        "| **problema**       | `Dict[str, float]` | Coeficientes QUBO en formato de clave de cadena                        | `{\"()\": -21.0, \"(0,4)\": 0.5, \"(0,1)\": 0.5}` |\n",
        "| **tipo\\_problema** | `str`              | Especificación del formato: `\"binary\"` para QUBO o `\"spin\"` para Ising | `\"binary\"`                                  |\n",
        "| **backend\\_name**  | `str`              | Dispositivo cuántico objetivo                                          | `\"ibm_fez\"`                                 |\n",
        "\n",
        "<span id=\"essential-concepts\" />\n",
        "\n",
        "#### Conceptos esenciales\n",
        "\n",
        "* **Formato del problema** : Utilizamos `\"binary\"` ya que nuestras variables son binarias (0/1), representando asignaciones de mercado.\n",
        "* **Selección del backend** : Elija entre las QPU disponibles (por ejemplo, `\"ibm_fez\"`) en función de sus necesidades y de la instancia de recursos informáticos.\n",
        "* **Estructura QUBO** : Nuestro diccionario de problemas contiene los coeficientes exactos de la transformación matemática.\n",
        "\n",
        "<span id=\"advanced-options-optional\" />\n",
        "\n",
        "#### Opciones avanzadas (opcional)\n",
        "\n",
        "Iskay ofrece posibilidades de ajuste mediante parámetros opcionales. Aunque los valores por defecto funcionan bien para la mayoría de los problemas, puede personalizar el comportamiento para requisitos específicos:\n",
        "\n",
        "| Parámetro                    | Tipo        | Valor predeterminado | Descripción                                                                          |\n",
        "| ---------------------------- | ----------- | -------------------- | ------------------------------------------------------------------------------------ |\n",
        "| **Intentos**                 | `int`       | 10000                | Mediciones cuánticas por iteración (mayor = más preciso)                             |\n",
        "| **número\\_interacciones**    | `int`       | 10                   | Iteraciones del algoritmo (más iteraciones pueden mejorar la calidad de la solución) |\n",
        "| **utilizar\\_sesión**         | `bool`      | Sí                   | Utilice las sesiones de IBM para reducir los tiempos de espera                       |\n",
        "| **seed\\_transpiler**         | `int`       | Ninguna              | Set para la compilación reproducible de circuitos cuánticos                          |\n",
        "| **direct\\_qubit\\_mapping**   | `bool`      | No                   | Asignar qubits virtuales directamente a qubits físicos                               |\n",
        "| **job\\_tags**                | `List[str]` | Ninguna              | Etiquetas personalizadas para el seguimiento de trabajos                             |\n",
        "| **preprocesamiento\\_nivel**  | `int`       | 0                    | Intensidad de preprocesamiento del problema (0-3) - ver detalles más abajo           |\n",
        "| **postprocesamiento\\_nivel** | `int`       | 2                    | Nivel de refinamiento de la solución (0-2) - ver detalles más abajo                  |\n",
        "| **transpilation\\_level**     | `int`       | 0                    | Pruebas de optimización del transpilador (0-5) - ver detalles más abajo              |\n",
        "| **transpile\\_only**          | `bool`      | No                   | Analizar la optimización de circuitos sin ejecutar la versión completa               |\n",
        "\n",
        "**Niveles de preprocesamiento (0-3)** : Especialmente importante para problemas más grandes que actualmente no caben en los tiempos de coherencia del hardware. Los niveles de preprocesamiento más altos consiguen una menor profundidad de los circuitos mediante aproximaciones en la transpilación del problema:\n",
        "\n",
        "* **Nivel 0** : Circuitos exactos y más largos\n",
        "* **Nivel 1** : Buen equilibrio entre precisión y aproximación, recortando sólo las puertas con ángulos en el percentil 10 más bajo\n",
        "* **Nivel 2** : Aproximación ligeramente superior, recortando las puertas con ángulos en el percentil 20 más bajo y utilizando `approximation_degree=0.95` en la transpilación\n",
        "* **Nivel 3** : Nivel de aproximación máxima, recortando las puertas del percentil 30 más bajo y utilizando `approximation_degree=0.90` en la transpilación\n",
        "\n",
        "**Niveles de transpilación (0-5)** : Controla los ensayos avanzados de optimización del transpilador para la compilación de circuitos cuánticos. Esto puede suponer un aumento de la sobrecarga clásica y, en algunos casos, puede que no cambie la profundidad del circuito. El valor por defecto `2` en general conduce al circuito más pequeño y es relativamente rápido.\n",
        "\n",
        "* **Nivel 0** : Optimización del circuito DCQO descompuesto (diseño, encaminamiento, programación)\n",
        "* **Nivel 1** : Optimización de `PauliEvolutionGate` y, a continuación, del circuito DCQO descompuesto ( max\\_trials=10 )\n",
        "* **Nivel 2** : Optimización de `PauliEvolutionGate` y, a continuación, del circuito DCQO descompuesto ( max\\_trials=15 )\n",
        "* **Nivel 3** : Optimización de `PauliEvolutionGate` y, a continuación, del circuito DCQO descompuesto ( max\\_trials=20 )\n",
        "* **Nivel 4** : Optimización de `PauliEvolutionGate` y, a continuación, del circuito DCQO descompuesto ( max\\_trials=25 )\n",
        "* **Nivel 5** : Optimización de `PauliEvolutionGate` y, a continuación, del circuito DCQO descompuesto ( max\\_trials=50 )\n",
        "\n",
        "**Niveles de postprocesamiento (0-2)** : Controla la cantidad de optimización clásica, compensando los errores de bit-flip con diferente número de pasadas codiciosas de una búsqueda local:\n",
        "\n",
        "* **Nivel 0** : 1 aprobado\n",
        "* **Nivel 1** : 2 pases\n",
        "* **Nivel 2** : 3 pases\n",
        "\n",
        "**Modo sólo transpile** : Ahora disponible para usuarios que deseen analizar la optimización de circuitos sin ejecutar el algoritmo cuántico completo.\n",
        "\n",
        "<span id=\"custom-configuration-example\" />\n",
        "\n",
        "#### Ejemplo de configuración personalizada\n",
        "\n",
        "A continuación te mostramos cómo podrías configurar Iskay con diferentes ajustes:\n",
        "\n",
        "```python\n",
        "custom_options = {\n",
        "    # Higher shot count for better statistics\n",
        "    \"shots\": 15_000,\n",
        "\n",
        "    # More iterations for solution refinement\n",
        "    \"num_iterations\": 12,\n",
        "\n",
        "    # Light preprocessing for problem simplification\n",
        "    \"preprocessing_level\": 1,\n",
        "\n",
        "    # Maximum postprocessing for solution quality\n",
        "    \"postprocessing_level\": 2,\n",
        "\n",
        "    # Using higher transpilation level for circuit optimization\n",
        "    \"transpilation_level\": 3,\n",
        "\n",
        "    # Fixed seed for reproducible results\n",
        "    \"seed_transpiler\": 42,\n",
        "\n",
        "    # Custom tracking tags\n",
        "    \"job_tags\": [\"market_split\"]\n",
        "}\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "86e2b10d",
      "metadata": {},
      "source": [
        "Para este tutorial, mantendremos la mayoría de los parámetros por defecto y sólo cambiaremos el número de iteraciones del campo de polarización:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "config",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Specify the target backend\n",
        "backend_name = \"ibm_fez\"\n",
        "\n",
        "# Set the number of bias-field iterations and set a tag to identify the jobs\n",
        "options = {\n",
        "    \"num_iterations\": 3,  # Change number of bias-field iterations\n",
        "    \"job_tags\": [\"market_split_example\"],  # Tag to identify jobs\n",
        "}\n",
        "\n",
        "# Configure Iskay optimizer\n",
        "iskay_input = {\n",
        "    \"problem\": iskay_input_problem,\n",
        "    \"problem_type\": \"binary\",\n",
        "    \"backend_name\": backend_name,\n",
        "    \"options\": options,\n",
        "}\n",
        "\n",
        "print(\"Iskay Optimizer Configuration:\")\n",
        "print(\"=\" * 40)\n",
        "print(f\"  Backend: {backend_name}\")\n",
        "print(f\"  Problem: {len(iskay_input['problem'])} terms\")\n",
        "print(\"  Algorithm: bf-DCQO\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "submit_intro",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Paso 3: Ejecutar utilizando Qiskit primitives\n",
        "\n",
        "Ahora presentamos nuestro problema para que se ejecute en el hardware IBM Quantum. El algoritmo bf-DCQO lo hará:\n",
        "\n",
        "1. Construir circuitos cuánticos poco profundos con términos contradiabáticos\n",
        "2. Ejecutar aproximadamente 10 iteraciones con optimización del campo de polarización\n",
        "3. Realizar el postprocesamiento clásico con búsqueda local\n",
        "4. Devolver la asignación óptima al mercado\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "run",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Submit the optimization job\n",
        "print(\"Submitting optimization job to Kipu Quantum...\")\n",
        "print(\n",
        "    f\"Problem size: {A.shape[1]} variables, {len(iskay_input['problem'])} terms\"\n",
        ")\n",
        "print(\n",
        "    \"Algorithm: bf-DCQO (bias-field digitized counterdiabatic quantum optimization)\"\n",
        ")\n",
        "\n",
        "job = iskay_solver.run(**iskay_input)\n",
        "\n",
        "print(\"\\nJob successfully submitted!\")\n",
        "print(f\"Job ID: {job.job_id}\")\n",
        "print(\"Optimization in progress...\")\n",
        "print(\n",
        "    f\"The bf-DCQO algorithm will efficiently explore \"\n",
        "    f\"{2**A.shape[1]:,} possible assignments\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "status_intro",
      "metadata": {},
      "source": [
        "<span id=\"monitor-job-status\" />\n",
        "\n",
        "### Supervisar el estado del trabajo\n",
        "\n",
        "Puede comprobar el estado actual de su trabajo de optimización. Los estados posibles son:\n",
        "\n",
        "* `QUEUED`: El trabajo está esperando en la cola\n",
        "* `RUNNING`: El trabajo se está ejecutando actualmente en hardware cuántico\n",
        "* `DONE`: Trabajo completado con éxito\n",
        "* `CANCELED`: Trabajo cancelado\n",
        "* `ERROR`: El trabajo ha encontrado un error\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "status",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Check job status\n",
        "print(f\"Job status: {job.status()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "wait_intro",
      "metadata": {},
      "source": [
        "<span id=\"wait-for-completion\" />\n",
        "\n",
        "### Espere hasta que finalice\n",
        "\n",
        "Esta celda se bloqueará hasta que se complete el trabajo. El proceso de optimización incluye:\n",
        "\n",
        "* Tiempo de espera (espera de acceso al hardware cuántico)\n",
        "* Tiempo de ejecución (ejecutando el algoritmo bf-DCQO con aproximadamente 10 iteraciones)\n",
        "* Tiempo de postprocesamiento (búsqueda local clásica)\n",
        "\n",
        "Los tiempos típicos de finalización oscilan entre unos minutos y decenas de minutos, dependiendo de las condiciones de la cola.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "wait",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Wait for job completion\n",
        "while True:\n",
        "    status = job.status()\n",
        "    print(\n",
        "        f\"Waiting for job {job.job_id} to complete... (status: {status})\",\n",
        "        end=\"\\r\",\n",
        "        flush=True,\n",
        "    )\n",
        "    if status in [\"DONE\", \"CANCELED\", \"ERROR\"]:\n",
        "        print(\n",
        "            f\"\\nJob {job.job_id} completed with status: {status}\" + \" \" * 20\n",
        "        )\n",
        "        break\n",
        "    time.sleep(30)\n",
        "\n",
        "# Retrieve the optimization results\n",
        "result = job.result()\n",
        "print(\"\\nOptimization complete!\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step5",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## Paso 4: Procesamiento posterior y devolución del resultado en el formato clásico deseado\n",
        "\n",
        "Ahora post-procesamos los resultados de la ejecución cuántica. Esto incluye:\n",
        "\n",
        "* Análisis de la estructura de la solución\n",
        "* Validación del cumplimiento de las restricciones\n",
        "* Comparación con los enfoques clásicos\n",
        "\n",
        "<span id=\"analyze-results\" />\n",
        "\n",
        "### Analizar los resultados\n",
        "\n",
        "<span id=\"understand-the-result-structure\" />\n",
        "\n",
        "#### Comprender la estructura del resultado\n",
        "\n",
        "Iskay devuelve un completo diccionario de resultados que contiene:\n",
        "\n",
        "* **`solution`**: Un diccionario que asigna los índices de las variables a sus valores óptimos (0 ó 1)\n",
        "* **`solution_info`**: Información detallada que incluye:\n",
        "  * `bitstring`: La asignación óptima como cadena binaria\n",
        "  * `cost`: El valor de la función objetivo (debe ser 0 para una satisfacción perfecta de las restricciones)\n",
        "  * `mapping`: Cómo se asignan las posiciones de las cadenas de bits a las variables del problema\n",
        "  * `seed_transpiler`: Semilla utilizada para la reproducibilidad\n",
        "* **`prob_type`**: Si la solución está en formato binario o de giro\n",
        "\n",
        "Examinemos la solución devuelta por el optimizador cuántico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "results",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Display the optimization results\n",
        "print(\"Optimization Results\")\n",
        "print(\"=\" * 50)\n",
        "print(f\"Problem Type: {result['prob_type']}\")\n",
        "print(\"\\nSolution Info:\")\n",
        "print(f\"  Bitstring: {result['solution_info']['bitstring']}\")\n",
        "print(f\"  Cost: {result['solution_info']['cost']}\")\n",
        "print(\"\\nSolution (first 10 variables):\")\n",
        "for i, (var, val) in enumerate(list(result[\"solution\"].items())[:10]):\n",
        "    print(f\"  {var}: {val}\")\n",
        "print(\"  ...\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "validation_intro",
      "metadata": {},
      "source": [
        "<span id=\"solution-validation\" />\n",
        "\n",
        "#### Validación de la Solución\n",
        "\n",
        "Ahora validamos si la solución cuántica satisface las restricciones de la división del mercado. El proceso de validación comprueba:\n",
        "\n",
        "**¿Qué es una violación de una restricción?**\n",
        "\n",
        "* Para cada producto $i$, calculamos las ventas reales en la región A: $(Ax)_i$\n",
        "* Lo comparamos con el objetivo de ventas $b_i$\n",
        "* La **violación** es la diferencia absoluta: $|(Ax)_i - b_i|$\n",
        "* Una **solución viable** tiene cero violaciones para todos los productos\n",
        "\n",
        "**Lo que esperamos:**\n",
        "\n",
        "* **Caso ideal** : Violación total = 0 (todas las restricciones se cumplen perfectamente)\n",
        "  * La región A recibe exactamente 1002 unidades del producto 1, 879 unidades del producto 2 y 1040 unidades del producto 3\n",
        "  * La Región B se queda con las unidades restantes (también 1002, 879 y 1040 respectivamente)\n",
        "* **Buen caso** : La violación total es pequeña (solución casi óptima)\n",
        "* **Caso deficiente** : Las infracciones graves indican que la solución no satisface los requisitos de la empresa\n",
        "\n",
        "La función de validación calculará:\n",
        "\n",
        "1. Ventas reales por producto en cada región\n",
        "2. Infracciones de las restricciones para cada producto\n",
        "3. Distribución del mercado entre regiones\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "validate",
      "metadata": {},
      "outputs": [],
      "source": [
        "def validate_solution(A, b, solution):\n",
        "    \"\"\"Validate market split solution.\"\"\"\n",
        "    x = np.array(solution)\n",
        "    region_a = A @ x\n",
        "    region_b = A @ (1 - x)\n",
        "    violations = np.abs(region_a - b)\n",
        "\n",
        "    return {\n",
        "        \"target\": b,\n",
        "        \"region_a\": region_a,\n",
        "        \"region_b\": region_b,\n",
        "        \"violations\": violations,\n",
        "        \"total_violation\": np.sum(violations),\n",
        "        \"is_feasible\": np.sum(violations) == 0,\n",
        "        \"region_a_markets\": int(np.sum(x)),\n",
        "        \"region_b_markets\": len(x) - int(np.sum(x)),\n",
        "    }\n",
        "\n",
        "\n",
        "# Convert bitstring to list of integers and validate\n",
        "optimal_assignment = [\n",
        "    int(bit) for bit in result[\"solution_info\"][\"bitstring\"]\n",
        "]\n",
        "validation = validate_solution(A, b, optimal_assignment)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "validation_results_intro",
      "metadata": {},
      "source": [
        "<span id=\"interpret-the-validation-results\" />\n",
        "\n",
        "#### Interprete los resultados de la validación\n",
        "\n",
        "Los resultados de la validación muestran si el Optimizador Cuántico encontró una solución factible. Examinemos lo siguiente:\n",
        "\n",
        "**Comprobación de viabilidad:**\n",
        "\n",
        "* **`is_feasible = True`** significa que la solución satisface perfectamente todas las restricciones (violación total = 0)\n",
        "* **`is_feasible = False`** significa que se infringen algunas restricciones\n",
        "\n",
        "**Análisis de ventas:**\n",
        "\n",
        "* Comparar las ventas previstas con las reales de cada producto\n",
        "* Para una solución perfecta Real = Objetivo para todos los productos en ambas regiones\n",
        "* La diferencia indica lo cerca que estamos de la división deseada del mercado\n",
        "\n",
        "**Distribución en el mercado:**\n",
        "\n",
        "* Muestra cuántos mercados están asignados a cada región\n",
        "* No se exige el mismo número de mercados, sólo que se cumplan los objetivos de ventas\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "display_validation",
      "metadata": {},
      "outputs": [],
      "source": [
        "print(\"Solution Validation\")\n",
        "print(\"=\" * 50)\n",
        "print(f\"Feasible solution: {validation['is_feasible']}\")\n",
        "print(f\"Total constraint violation: {validation['total_violation']}\")\n",
        "\n",
        "print(\"\\nSales Analysis (Target vs Actual):\")\n",
        "for i, (target, actual_a, actual_b) in enumerate(\n",
        "    zip(validation[\"target\"], validation[\"region_a\"], validation[\"region_b\"])\n",
        "):\n",
        "    violation_a = abs(actual_a - target)\n",
        "    violation_b = abs(actual_b - target)\n",
        "    print(f\"  Product {i+1}:\")\n",
        "    print(f\"    Target: {target}\")\n",
        "    print(f\"    Region A: {actual_a} (violation: {violation_a})\")\n",
        "    print(f\"    Region B: {actual_b} (violation: {violation_b})\")\n",
        "\n",
        "print(\"\\nMarket Distribution:\")\n",
        "print(f\"  Region A: {validation['region_a_markets']} markets\")\n",
        "print(f\"  Region B: {validation['region_b_markets']} markets\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "interpretation",
      "metadata": {},
      "source": [
        "<span id=\"solution-quality-assessment\" />\n",
        "\n",
        "#### Evaluación de la calidad de la solución\n",
        "\n",
        "Basándonos en los resultados de validación anteriores, podemos evaluar la calidad de la solución cuántica:\n",
        "\n",
        "**Si `is_feasible = True` (Violación total = 0):**\n",
        "\n",
        "* El Optimizador Cuántico encontró con éxito una solución óptima\n",
        "* Se satisfacen perfectamente todas las exigencias de la empresa\n",
        "* Esto demuestra la ventaja cuántica en un problema en el que los solucionadores clásicos tienen dificultades [\\[4\\]](#references)\n",
        "\n",
        "**Si `is_feasible = False` (Violación total > 0):**\n",
        "\n",
        "* La solución es casi óptima pero no perfecta\n",
        "* Las pequeñas infracciones pueden ser aceptables en la práctica\n",
        "* Considere la posibilidad de ajustar los parámetros del optimizador:\n",
        "  * Aumentar `num_iterations` para más pases de optimización\n",
        "  * Aumentar `postprocessing_level` para un refinamiento más clásico\n",
        "  * Aumentar `shots` para obtener mejores estadísticas de medición\n",
        "\n",
        "**Interpretación de la función de costes:**\n",
        "\n",
        "* El valor `cost` de `solution_info` es igual a $||Ax - b||^2$\n",
        "* Coste = 0: satisfacción perfecta de las restricciones\n",
        "* Los valores de coste más altos indican mayores violaciones de las restricciones\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "conclusion",
      "metadata": {},
      "source": [
        "<span id=\"conclusion\" />\n",
        "\n",
        "## Conclusión\n",
        "\n",
        "<span id=\"what-we-accomplished\" />\n",
        "\n",
        "### Lo que hemos logrado\n",
        "\n",
        "En este tutorial, con éxito:\n",
        "\n",
        "1. **Carga de un problema de optimización real** : Obtención de una instancia de Market Split de la biblioteca de pruebas QOBLIB \\[2]\n",
        "2. **Transformado a formato QUBO** : Conversión del problema restringido en una formulación cuadrática no restringida \\[3]\n",
        "3. **Aprovechamiento de algoritmos cuánticos avanzados** : Utilizado el algoritmo bf-DCQO de Kipu Quantum con términos contradiabáticos \\[1]\n",
        "4. **Obtención de soluciones óptimas** : Encontradas soluciones factibles que satisfacen todas las restricciones\n",
        "\n",
        "<span id=\"key-takeaways\" />\n",
        "\n",
        "### Principales conclusiones\n",
        "\n",
        "**Innovación del algoritmo** : El algoritmo bf-DCQO representa un avance significativo [\\[1\\]](#references) :\n",
        "\n",
        "* **10 veces menos puertas** que el recocido cuántico digital\n",
        "* **Aproximadamente 10 iteraciones** en lugar de las aproximadamente 100 de los métodos variacionales\n",
        "* **Resistencia a errores integrada** gracias a la eficiencia de los circuitos\n",
        "\n",
        "**Términos contradiabáticos** : Permiten una rápida evolución cuántica manteniendo la fidelidad al estado terreno, lo que hace que la optimización cuántica sea práctica en el ruidoso hardware actual [\\[1\\]](#references).\n",
        "\n",
        "**Orientación por campo de polarización** : El enfoque iterativo del campo de polarización permite que cada iteración comience cerca de las buenas soluciones encontradas previamente, proporcionando una forma de búsqueda local mejorada cuánticamente [\\[1\\]](#references).\n",
        "\n",
        "<span id=\"next-steps\" />\n",
        "\n",
        "### Próximos pasos\n",
        "\n",
        "Para profundizar en su comprensión y explorar más a fondo:\n",
        "\n",
        "1. **Pruebe diferentes instancias** : Experimente con otras instancias de QOBLIB de distintos tamaños\n",
        "2. **Ajuste los parámetros** : Ajuste `num_iterations`, `preprocessing_level`, `postprocessing_level`\n",
        "3. **Comparación con los** métodos clásicos: Comparación con los métodos clásicos de optimización\n",
        "4. **Pruebe diferentes estrategias** : Intentar encontrar una codificación mejor para el problema o formularlo como HUBO (si es posible)\n",
        "5. Aplíquelo **a su dominio** : Adapte las técnicas de formulación QUBO/HUBO a sus propios problemas de optimización\n",
        "\n",
        "<span id=\"references\" />\n",
        "\n",
        "### Referencias\n",
        "\n",
        "\\[1] IBM Quantum. \"[Optimización Cuántica Kipu](/docs/guides/kipu-optimization) \" *IBM Quantum Documentación*.\n",
        "\n",
        "\\[2] QOBLIB - Biblioteca de evaluación comparativa de optimización cuántica. Instituto Zuse de Berlín (ZIB). [https://git.zib.de/qopt/qoblib-quantum-optimization-benchmarking-library](https://git.zib.de/qopt/qoblib-quantum-optimization-benchmarking-library)\n",
        "\n",
        "\\[3] Glover, F., Kochenberger, G., & Du, Y. (2019). \"Quantum bridge analytics I: a tutorial on formulating and using QUBO models\" *4OR: Revista trimestral de investigación operativa*, 17(4), 335-371.\n",
        "\n",
        "\\[4] Lodi, A., Tramontani, A., y Weninger, K. (2023). \"El Decatlón Intratable: Benchmarking Hard Combinatorial Problems\" *INFORMS Journal on Computing*.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1,
    "qpuSeconds": 20
  },
  "nbformat": 4,
  "nbformat_minor": 5
}