{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b6d1e3ec",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Resuelve el problema de la segmentación del mercado con el optimizador Parity Twine de ParityQC\"\n",
        "description: \"Descubre cómo resolver el problema de la división del mercado utilizando el Parity Twine Optimizer.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore parityqc QOBLIB marketsplit independentset */}\n",
        "\n",
        "<span id=\"solve-the-market-split-problem-with-the-parityqc-parity-twine-optimizer\" />\n",
        "\n",
        "# Resuelve el problema de la segmentación del mercado con el optimizador Parity Twine de ParityQC\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a6f69b77",
      "metadata": {},
      "source": [
        "Estimación *de tiempo de ejecución: 10 segundos en un procesador Nighthawk r2. (NOTA: Se trata únicamente de una estimación. (El tiempo de ejecución puede variar.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "21156b6f",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Resultados del aprendizaje\n",
        "\n",
        "* Consigue y formatea el problema «Market Split» de la biblioteca [QOBLIB (Quantum Optimization Benchmarking Library)](https://github.com/ZIB-AOPT/QOBLIB).\n",
        "* Configura y utiliza el Parity Twine Optimizer para resolver un caso de «Market Split».\n",
        "* Descubre cómo seleccionar las opciones del optimizador Parity Twine y qué resultados se obtienen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d185259f257c1618",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## En segundo plano\n",
        "\n",
        "Este tutorial muestra cómo resolver el problema de la división del mercado utilizando el optimizador «Parity Twine» de ParityQC.\n",
        "\n",
        "El ejemplo del problema procede de [QOBLIB (Quantum Optimization Benchmarking Library)](https://github.com/ZIB-AOPT/QOBLIB).\n",
        "\n",
        "<span id=\"market-split-problem\" />\n",
        "\n",
        "### Problema de división del mercado\n",
        "\n",
        "El problema de la división del mercado es un problema real de asignación de recursos de complejidad NP-difícil y se ha convertido en un punto de referencia para los algoritmos de optimización cuántica.\n",
        "Supone un reto logístico de gran envergadura: cómo dividir un panorama complejo de clientes y productos en territorios manejables y equilibrados.\n",
        "\n",
        "El objetivo es dividir los mercados de $n$ en dos regiones de ventas equilibradas, de modo que cada región reciba exactamente la mitad de la demanda total de los productos de $m$.\n",
        "La solución consiste en la configuración específica que permite lograr la distribución más uniforme posible de la demanda de productos, lo que permite a una empresa aplicar una estrategia logística y de dotación de personal en la que ambas regiones estén equilibradas,\n",
        "minimizando así riesgos como la escasez localizada de productos o el desbordamiento de los almacenes.\n",
        "\n",
        "A medida que aumenta el número de mercados y productos, el número de permutaciones posibles crece exponencialmente, lo que dificulta encontrar la mejor distribución mediante búsquedas exhaustivas tradicionales.\n",
        "\n",
        "<span id=\"mathematical-formulation\" />\n",
        "\n",
        "### Formulación matemática\n",
        "\n",
        "Sea $A$ una matriz de tipo $m \\times n$ que representa la demanda de productos en los distintos mercados, donde $A_{ij}$ es la demanda del producto $i$ en el mercado $j$.\n",
        "\n",
        "Se define un vector de asignación binario, $x = [x_1, x_2, \\dots, x_n]^T \\in \\{0, 1\\}^n$, en el que:\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",
        "\n",
        "Sea $d = [d_1, d_2, \\dots, d_m]^T$ el vector de demanda total de cada producto, calculado como $d = A \\cdot \\mathbf{1}$. El volumen de ventas objetivo por región para el producto $i$ es exactamente $\\frac{d_i}{2}$.\n",
        "\n",
        "La restricción de optimización o viabilidad exige que las ventas totales asignadas a la Región A coincidan exactamente con la mitad de la demanda total de cada producto:\n",
        "\n",
        "$A x = \\frac{1}{2} A \\mathbf{1} = b.$\n",
        "\n",
        "En la práctica, dado que rara vez es posible realizar una división exacta, el problema se formula para minimizar el cuadrado de la desviación respecto a la restricción (la función de coste):\n",
        "\n",
        "$\\min_{x} \\left\\Vert{} A x - b \\right\\Vert{}^2 = \\sum_{i=1}^{m} \\left( \\sum_{j=1}^{n} A_{ij} x_j - b\\right)^2.$\n",
        "\n",
        "Al desarrollar esto, se obtiene una forma equivalente a un problema de optimización binaria cuadrática sin restricciones (QUBO).\n",
        "\n",
        "Una vez resuelta la ecuación, el vector de solución $x$ determina a qué región se asigna el mercado. Esta es la configuración que permite lograr la distribución más equilibrada posible de la demanda de productos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de comenzar este tutorial, asegúrate de que tengas instalados los siguientes elementos:\n",
        "\n",
        "* Qiskit Functions Catalog IBM Cliente (`pip install qiskit-ibm-catalog`)\n",
        "* Complemento de Qiskit «Optimization Mapper» (`pip install qiskit_addon_opt_mapper`)\n",
        "* NumPy (`pip install numpy`)\n",
        "\n",
        "También necesitas permiso para acceder a la función « ParityQC » de Twine Optimizer. Para solicitar acceso, rellena este [formulario](https://parityqc.com/products/parity-twine-optimizer/free-trial).\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuración\n",
        "\n",
        "(Este código da por hecho que ya has [guardado tu cuenta](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) en tu entorno local.)\n",
        "\n",
        "En primer lugar, importa todos los paquetes necesarios para este tutorial.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [],
      "source": [
        "import tempfile\n",
        "\n",
        "from collections.abc import Callable\n",
        "from pathlib import Path\n",
        "\n",
        "import numpy as np\n",
        "import requests\n",
        "\n",
        "from qiskit_addon_opt_mapper import OptimizationProblem\n",
        "from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d59c6bf33095b413",
      "metadata": {},
      "source": [
        "Carga el «Parity Twine Optimizer» del catálogo « Qiskit Functions »:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "805c80a1180e79fe",
      "metadata": {},
      "outputs": [],
      "source": [
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "function = catalog.load(\"parityqc/parity-twine-optimizer\")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "metadata": {},
      "source": [
        "<span id=\"step-1-define-the-problem-as-an-objective-function\" />\n",
        "\n",
        "### Paso 1: Definir el problema como una función objetivo\n",
        "\n",
        "Obtén un ejemplo de problema de división de mercado de la biblioteca [QOBLIB (Quantum Optimization Benchmarking Library)](https://github.com/ZIB-AOPT/QOBLIB) de la siguiente manera.\n",
        "\n",
        "La función `load_market_split_problem` recupera un problema determinado de QOBLIB y lo convierte en un problema QUBO.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2a90bce6-1925-436f-a167-4fab3255f4a0",
      "metadata": {},
      "outputs": [],
      "source": [
        "def load_market_split_problem(instance_name: str) -> OptimizationProblem:\n",
        "    \"\"\"Load and formulate a market split optimization problem from an QOBLIB instance.\n",
        "\n",
        "    The QOBLIB library can be found here:\n",
        "    https://github.com/ZIB-AOPT/QOBLIB.\n",
        "\n",
        "    Args:\n",
        "        instance_name: Name of the market split instance to load as specified by the .dat file\n",
        "            in the QOBLIB repo.\n",
        "\n",
        "    Returns:\n",
        "        The output OptimizationProblem containing the loaded market split problem.\n",
        "    \"\"\"\n",
        "\n",
        "    problem_matrix, problem_vector = fetch_and_parse(\n",
        "        instance_name, \"01-marketsplit\", parse_marketsplit_dat\n",
        "    )\n",
        "\n",
        "    # Create optimization problem\n",
        "    optimization_problem = OptimizationProblem(instance_name)\n",
        "\n",
        "    # Add binary variables (one for each market)\n",
        "    optimization_problem.binary_var_list(problem_matrix.shape[1])\n",
        "\n",
        "    # Add equality constraints (one for each product)\n",
        "    for idx, rhs in enumerate(problem_vector):\n",
        "        optimization_problem.linear_constraint(\n",
        "            problem_matrix[idx, :], sense=\"==\", rhs=rhs\n",
        "        )\n",
        "\n",
        "    # Convert to QUBO with penalty parameter\n",
        "    return OptimizationProblemToQubo(penalty=1).convert(optimization_problem)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "becd6460-8eb9-4796-8a21-60d6edc56cd1",
      "metadata": {},
      "source": [
        "La función `load_market_split_problem` requiere las siguientes funciones de análisis sintáctico para recuperar y procesar los datos del problema de división de mercados procedentes de QOBLIB.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c07d1d1b-3dc1-4fd8-b7cd-b7898e974eee",
      "metadata": {},
      "outputs": [],
      "source": [
        "def fetch_and_parse(instance_name: str, problem: str, parse_func: Callable):\n",
        "    \"\"\"Generic function to fetch and parse data from QOBLIB repository.\n",
        "\n",
        "    Args:\n",
        "        instance_name: Name of the instance to fetch.\n",
        "        problem: Category of the problem (e.g., '01-marketsplit', '07-independentset').\n",
        "        parse_func: Function used to parse the downloaded file\n",
        "            (e.g., parse_marketsplit_dat, parse_gph_file).\n",
        "\n",
        "    Returns:\n",
        "        Result of `parse_func` - either (np.ndarray, np.ndarray) for marketsplit\n",
        "        or nx.Graph for MIS.\n",
        "    \"\"\"\n",
        "    base_url = (\n",
        "        \"https://raw.githubusercontent.com/ZIB-AOPT/QOBLIB/refs/heads/main/\"\n",
        "    )\n",
        "    url = (\n",
        "        base_url\n",
        "        + problem\n",
        "        + \"/instances/\"\n",
        "        + instance_name\n",
        "        + (\".dat\" if problem == \"01-marketsplit\" else \".gph\")\n",
        "    )\n",
        "\n",
        "    try:\n",
        "        response = requests.get(url, timeout=30)\n",
        "        response.raise_for_status()\n",
        "\n",
        "        with tempfile.NamedTemporaryFile(\n",
        "            mode=\"w\",\n",
        "            suffix=\".dat\" if problem == \"01-marketsplit\" else \".gph\",\n",
        "            delete=False,\n",
        "            encoding=\"utf-8\",\n",
        "        ) as temp_file:\n",
        "            temp_file.write(response.text)\n",
        "            temp_file_path = temp_file.name\n",
        "\n",
        "        try:\n",
        "            return parse_func(temp_file_path)\n",
        "        finally:\n",
        "            Path(temp_file_path).unlink(missing_ok=True)\n",
        "\n",
        "    except requests.RequestException as e:\n",
        "        print(f\"Error fetching data from repository: {e}\")\n",
        "    except (ValueError, OSError) as e:\n",
        "        print(f\"Error processing data: {e}\")\n",
        "        return None\n",
        "\n",
        "\n",
        "def parse_marketsplit_dat(filename: str) -> tuple[np.ndarray, np.ndarray]:\n",
        "    \"\"\"Parse a market split problem from a .dat file format.\n",
        "\n",
        "    Args:\n",
        "        filename: Path to the .dat file.\n",
        "\n",
        "    Returns:\n",
        "        Tuple of (A, b) where:\n",
        "            - A: (m, n) array of coefficients.\n",
        "            - b: (m,) array of target values.\n",
        "\n",
        "    Raises:\n",
        "        ValueError: If file format is invalid or file is empty.\n",
        "    \"\"\"\n",
        "    with Path(filename).open(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",
        "    try:\n",
        "        m, n = map(int, lines[0].split())\n",
        "    except (ValueError, IndexError) as e:\n",
        "        raise ValueError(\n",
        "            \"Invalid file format: first line must contain 'm n' integers\"\n",
        "        ) from e\n",
        "\n",
        "    if len(lines) < m + 1:\n",
        "        raise ValueError(\n",
        "            f\"File contains {len(lines)} lines but expected {m + 1} lines\"\n",
        "        )\n",
        "\n",
        "    # Next m lines: each row of A followed by corresponding element of b\n",
        "    mat_a = []\n",
        "    vec_b = []\n",
        "\n",
        "    for i in range(1, m + 1):\n",
        "        try:\n",
        "            values = list(map(int, lines[i].split()))\n",
        "        except ValueError as e:\n",
        "            raise ValueError(f\"Invalid integer values in line {i + 1}\") from e\n",
        "\n",
        "        if len(values) != n + 1:\n",
        "            raise ValueError(\n",
        "                f\"Line {i + 1} contains {len(values)} values but expected {n + 1}\"\n",
        "            )\n",
        "\n",
        "        mat_a.append(values[:-1])  # First n values: product sales per market\n",
        "        vec_b.append(values[-1])  # Last value: target sales for this product\n",
        "\n",
        "    return np.array(mat_a), np.array(vec_b)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9811ef6c-717e-4033-963d-3fd85790be23",
      "metadata": {},
      "source": [
        "Una vez definido, se `load_marketsplit_problem` puede utilizar para cargar una instancia concreta del problema desde la biblioteca:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "287f6c52-9b2a-46b7-994d-7005bd4ab1c6",
      "metadata": {},
      "outputs": [],
      "source": [
        "ms_instance = \"ms_04_050_001\"\n",
        "\n",
        "ms_problem = load_market_split_problem(ms_instance)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "metadata": {},
      "source": [
        "<span id=\"step-2-convert-to-json-format\" />\n",
        "\n",
        "### Paso 2: Convertir al formato JSON\n",
        "\n",
        "En el primer paso, has obtenido la formulación QUBO del problema.  Ahora, conviértelo al formato JSON para la función del optimizador:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fe1169b1-50a0-4edb-9db5-7c0d99ce9a68",
      "metadata": {},
      "outputs": [],
      "source": [
        "def optimization_problem_to_json(\n",
        "    problem: OptimizationProblem,\n",
        ") -> dict[str, float]:\n",
        "    \"\"\"\n",
        "    Converts an unconstrained quadratic OptimizationProblem in terms of binary or spin variables\n",
        "    to the JSON input format of the Parity Twine Qiskit Function.\n",
        "\n",
        "    Args:\n",
        "        problem: The optimization problem to convert to JSON.\n",
        "\n",
        "    Returns:\n",
        "        The JSON input format of the given problem.\n",
        "    \"\"\"\n",
        "    ising, constant = problem.to_ising()\n",
        "    output = {\"()\": float(constant)}\n",
        "    for op, coefficient in zip(ising.paulis, ising.coeffs, strict=True):\n",
        "        # Invert the label strings because Qiskit has opposite convention\n",
        "        qubits = tuple(\n",
        "            num\n",
        "            for num, pauli in enumerate(op.to_label()[::-1])\n",
        "            if pauli == \"Z\"\n",
        "        )\n",
        "        output[str(qubits)] = float(coefficient)\n",
        "    return output"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9e374de6-384a-497e-bcf1-c8617c1945ec",
      "metadata": {},
      "source": [
        "La instancia QUBO del problema «Market Split» se ha convertido ahora al formato JSON de la siguiente manera:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4885df90-4d15-4bc7-8130-ec1d9ece7670",
      "metadata": {},
      "outputs": [],
      "source": [
        "json_ms_problem = optimization_problem_to_json(ms_problem)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "metadata": {},
      "source": [
        "<span id=\"step-3-solve-the-problem-using-the-parity-twine-optimizer\" />\n",
        "\n",
        "### Paso 3: Resuelve el problema utilizando el optimizador Parity Twine\n",
        "\n",
        "Ahora que ya has obtenido el problema de división del mercado y lo has convertido a la forma correcta, puedes hallar una solución utilizando el Optimizador de Twine y el backend de IBM® que hayas elegido.\n",
        "\n",
        "Para ejecutar la función, elige un dispositivo de fondo adecuado; por ejemplo, `ibm_phoenix`.\n",
        "\n",
        "Puedes utilizar `options` para tener un control adicional (opcional) sobre el envío:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a3eaff0a-9c54-4ffc-b6b2-5d43c49e6a07",
      "metadata": {},
      "outputs": [],
      "source": [
        "options = {\n",
        "    \"shots\": 100000,\n",
        "    \"postprocessing_level\": 1,\n",
        "    \"transpile_only\": False,\n",
        "    \"job_tags\": [\"market_split\"],\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "864629a2-34fe-473b-b9f1-277e0a99752d",
      "metadata": {},
      "source": [
        "donde es `shots` un número entero que especifica el número de ejecuciones del circuito, `postprocessing_level` determina si se aplica un posprocesamiento al resultado,\n",
        "`transpile_only`especifica si el problema solo se transpilaba a un circuito (y no se resuelve), y `job_tags` es la etiqueta utilizada para identificar el trabajo en IBM Quantum® Platform.\n",
        "\n",
        "Ejecuta el optimizador:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2c4a5ce3de728746",
      "metadata": {},
      "outputs": [],
      "source": [
        "function_job = function.run(\n",
        "    problem=json_ms_problem,\n",
        "    variable_type=\"spin\",\n",
        "    backend_name=\"ibm_phoenix\",\n",
        "    options=options,\n",
        ")\n",
        "print(f\"Job ID: {function_job.job_id}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e8e66a1efeef3757",
      "metadata": {},
      "source": [
        "Comprueba el estado del trabajo:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "be7bbe053da38198",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Monitor the job status\n",
        "function_job.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fe4ea9784df0bcbb",
      "metadata": {},
      "source": [
        "Obtener resultados:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9af5dddb30694451",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve the job result if the status is DONE\n",
        "result = function_job.result()\n",
        "\n",
        "result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8b888ac0-c0eb-4849-be0d-4bf331707df5",
      "metadata": {},
      "source": [
        "El resultado tiene la siguiente forma:\n",
        "\n",
        "```\n",
        "{\n",
        "    'solution': {'0': 1, '1': -1, '10': 1, ... },\n",
        "    'objective_value':  1.0,\n",
        "    'solution_bitstring': '010000011101111011001001110010',\n",
        "    'metadata': {\n",
        "        'circuit_metrics': {\n",
        "            'depth': 309,\n",
        "            'gate_count': 3880,\n",
        "            'two_qubit_gate_depth': 116,\n",
        "            'two_qubit_gate_count': 899,\n",
        "            'num_qubits': 30,\n",
        "            'operations': {'sx': 1244, 'rz': 1227, 'cz': 899, 'delay': 473, 'measure': 30, 'x': 7},\n",
        "        },\n",
        "        'solver_info': {\n",
        "            'variable_mapping': {'0': 0, '1': 1, '10': 2, ... },\n",
        "            'bitstring_distributions': {\n",
        "                'before_postprocessing': {'011101110010110111001110011000': 1, ...},\n",
        "                'after_postprocessing': {'011011110000110101001111011000': 1, ...}\n",
        "            },\n",
        "            'best_parameters': {\n",
        "                'beta': [-0.18054534155552715],\n",
        "                'gamma': [1.4141236348317905]\n",
        "            }\n",
        "        },\n",
        "        'resource_usage': {\n",
        "            'RUNNING: MAPPING': {'CPU_TIME': 172.936},\n",
        "            'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 0.272},\n",
        "            'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 7.798},\n",
        "            'RUNNING: EXECUTING_QPU': {'QPU_TIME': 30.0},\n",
        "            'RUNNING: POST_PROCESSING': {'CPU_TIME': 31.613},\n",
        "        },\n",
        "    }\n",
        "}\n",
        "```\n",
        "\n",
        "donde el diccionario `solution` se corresponde con los qubits definidos en el problema y proporciona sus valores de espín optimizados.\n",
        "`metadata` ofrece información sobre la transpilación (número de puertas de dos qubits/profundidad, puertas utilizadas, qubits activos) y diversos tiempos de ejecución.\n",
        "\n",
        "En el contexto del problema de la división del mercado, la cadena de bits de la solución representa un vector de asignación binario que se utiliza para dividir los mercados en dos regiones distintas. Un valor de 1 asigna ese\n",
        "mercado concreto a la Región A, mientras que un valor de 0 lo asigna a la Región B. En la solución óptima, la combinación equilibra el reparto, lo que significa que ambas regiones reciben exactamente la mitad de la demanda total de la empresa\n",
        "para cada producto.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2cb5785c",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  * Solicita acceso a la función rellenando este [formulario](https://parityqc.com/products/parity-twine-optimizer/free-trial).\n",
        "  * Consulta la [referencia](/docs/api/functions/parity-twine-optimizer) de la API de esta función de Qiskit.\n",
        "  * Lee la [guía](/docs/guides/parity-twine-optimizer).\n",
        "  * Prueba el [tutorial](/docs/tutorials/parity-twine-optimizer-sk) sobre cómo aplicar el «Parity Twine Optimizer» al modelo de Sherrington-Kirkpatrick.\n",
        "  * Consulta el artículo [«Connectivity-aware Synthesis of Quantum Algorithms», de Drier et al. (2025), disponible como](https://arxiv.org/abs/2501.14020) preimpresión en ArXiv.\n",
        "</Admonition>\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}