From d167bf62d302f8aa9e5136c2274eb884fd9150aa Mon Sep 17 00:00:00 2001 From: Murilo Oliveira Date: Thu, 23 Oct 2025 13:50:34 +0200 Subject: [PATCH] Add first version of the tutorial for solving the market split problem using Iskay Quantum Optimizer Qiskit Function --- ...problem_with_iskay_quantum_optimizer.ipynb | 1650 +++++++++++++++++ 1 file changed, 1650 insertions(+) create mode 100644 docs/tutorials/solve_market_split_problem_with_iskay_quantum_optimizer.ipynb diff --git a/docs/tutorials/solve_market_split_problem_with_iskay_quantum_optimizer.ipynb b/docs/tutorials/solve_market_split_problem_with_iskay_quantum_optimizer.ipynb new file mode 100644 index 00000000000..300eff2efac --- /dev/null +++ b/docs/tutorials/solve_market_split_problem_with_iskay_quantum_optimizer.ipynb @@ -0,0 +1,1650 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "title", + "metadata": {}, + "source": [ + "# Solve Market Split Problem with Kipu Quantum's Iskay Quantum Optimizer" + ] + }, + { + "cell_type": "markdown", + "id": "note", + "metadata": {}, + "source": [ + "\n", + "Qiskit Functions are an experimental feature available only to IBM Quantum® Premium Plan, Flex Plan, and On-Prem (via IBM Quantum Platform API) Plan users. They are in preview release status and subject to change.\n", + "\n", + "\n", + "*Usage estimate: TBD minutes on a Heron r2 processor. (NOTE: This is an estimate only. Your runtime might vary.)*" + ] + }, + { + "cell_type": "markdown", + "id": "background", + "metadata": {}, + "source": [ + "## Background\n", + "\n", + "This tutorial demonstrates how to solve the Market Split problem using [Kipu Quantum's Iskay quantum optimizer](/docs/guides/kipu-optimization) [1]. The Market Split problem represents a real-world resource allocation challenge where markets must be partitioned into balanced sales regions to meet exact demand targets.\n", + "\n", + "### The Market Split Challenge\n", + "\n", + "The Market Split problem presents a deceptively simple yet computationally formidable challenge in resource allocation. Consider a company with $m$ products being sold across $n$ different markets, where each market purchases a specific bundle of products (represented by the columns of matrix $A$). The business objective is to partition these markets into two balanced sales regions such that each region receives exactly half the total demand for every product.\n", + "\n", + "**Mathematical Formulation:**\n", + "\n", + "We seek a binary assignment vector $x$, where:\n", + "- $x_j = 1$ assigns market $j$ to Region A\n", + "- $x_j = 0$ assigns market $j$ to Region B \n", + "- The constraint $Ax = b$ must be satisfied, where $b$ represents the target sales (typically half the total demand per product)\n", + "\n", + "**Cost Function:**\n", + "\n", + "To solve this problem, we minimize the squared constraint violation:\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", + "where:\n", + "- $A_{ij}$ represents the sales of product $i$ in market $j$\n", + "- $x_j \\in \\{0,1\\}$ is the binary assignment of market $j$ \n", + "- $b_i$ is the target sales for product $i$ in each region\n", + "- The cost equals zero precisely when all constraints are satisfied\n", + "\n", + "Each term in the sum represents the squared deviation from the target sales for a particular product. When we expand this cost function, we get:\n", + "\n", + "$$C(x) = x^T A^T A x - 2b^T A x + b^T b$$\n", + "\n", + "Since $b^T b$ is a constant, minimizing $C(x)$ is equivalent to minimizing the quadratic function $x^T A^T A x - 2b^T A x$, which is exactly a QUBO (Quadratic Unconstrained Binary Optimization) problem.\n", + "\n", + "**Computational Complexity:**\n", + "\n", + "Despite its straightforward business interpretation, this problem exhibits remarkable computational intractability:\n", + "- **Small Scale Failure**: Conventional Mixed Integer Programming solvers fail on instances with as few as 7 products under a timeout of one hour [4]\n", + "- **Exponential Growth**: The solution space grows exponentially ($2^n$ possible assignments), making brute force approaches infeasible\n", + "\n", + "This severe computational barrier, combined with its practical relevance to territory planning and resource allocation, makes the Market Split problem an ideal benchmark for quantum optimization algorithms [4].\n", + "\n", + "### What Makes Iskay's Approach Unique?\n", + "\n", + "The Iskay optimizer uses the **bf-DCQO (bias-field digitized counterdiabatic quantum optimization)** algorithm [1], which represents a significant advancement in quantum optimization:\n", + "\n", + "**Circuit Efficiency**: The bf-DCQO algorithm achieves remarkable gate reduction [1]:\n", + "- Up to **10 times fewer entangling gates** than Digital Quantum Annealing (DQA)\n", + "- Significantly shallower circuits enable:\n", + " - Less error accumulation during quantum execution\n", + " - Ability to tackle larger problems on current quantum hardware \n", + " - No need for error mitigation techniques\n", + "\n", + "**Non-Variational Design**: Unlike variational algorithms requiring approximately 100 iterations, bf-DCQO typically needs only **approximately 10 iterations** [1]. This is achieved through:\n", + "- Intelligent bias-field calculations from measured state distributions\n", + "- Starting each iteration from an energy state near the previous solution\n", + "- Integrated classical post-processing with local search\n", + "\n", + "**Counterdiabatic Protocols**: The algorithm incorporates counterdiabatic terms that suppress unwanted quantum excitations during short evolution times, enabling the system to remain near the ground state even with rapid transitions [1]." + ] + }, + { + "cell_type": "markdown", + "id": "requirements", + "metadata": {}, + "source": [ + "## Requirements\n", + "\n", + "Before starting this tutorial, ensure that you have the following installed:\n", + "\n", + "* Qiskit IBM Runtime (`pip install qiskit-ibm-runtime`)\n", + "* Qiskit Functions (`pip install qiskit-ibm-catalog`)\n", + "* NumPy (`pip install numpy`)\n", + "* Requests (`pip install requests`)\n", + "\n", + "You will also need to get access to the Iskay Quantum Optimizer function from the Qiskit Functions Catalog." + ] + }, + { + "cell_type": "markdown", + "id": "setup", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First, import all required packages for this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All required libraries imported successfully\n" + ] + } + ], + "source": [ + "import os\n", + "import tempfile\n", + "import time\n", + "from typing import Tuple, Dict, Optional\n", + "\n", + "import numpy as np\n", + "import requests\n", + "\n", + "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n", + "\n", + "print(\"All required libraries imported successfully\")" + ] + }, + { + "cell_type": "markdown", + "id": "credentials", + "metadata": {}, + "source": [ + "### Configure IBM Quantum Credentials\n", + "\n", + "Define your [IBM Quantum Platform](https://quantum.cloud.ibm.com/) credentials. You will need:\n", + "- **API Token**: Your 44-character API key from IBM Quantum Platform\n", + "- **Instance CRN**: Your IBM Quantum instance identifier (format: `hub/group/project`)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "creds", + "metadata": {}, + "outputs": [], + "source": [ + "token = \"\"\n", + "instance = \"\"" + ] + }, + { + "cell_type": "markdown", + "id": "step1", + "metadata": {}, + "source": [ + "## Step 1: Connect to Iskay Quantum Optimizer\n", + "\n", + "We begin by establishing a connection to the Qiskit Functions Catalog and loading the Iskay quantum optimizer. The Iskay optimizer is a quantum function provided by Kipu Quantum that implements the bf-DCQO algorithm for solving optimization problems on quantum hardware." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "load_solver", + "metadata": {}, + "outputs": [], + "source": [ + "catalog = QiskitFunctionsCatalog(token=token)\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": [ + "## Step 2: Load and Formulate the Problem\n", + "\n", + "### Understanding the Problem Data Format\n", + "\n", + "Problem instances from QOBLIB (Quantum Optimization Benchmarking Library) [2] are stored in a simple text format. Let's examine the actual content of our target instance `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", + "**Format Structure:**\n", + "- **First line:** `3 20`\n", + " - `3` = number of products (constraints/rows in matrix $A$)\n", + " - `20` = number of markets (variables/columns in matrix $A$)\n", + "\n", + "- **Next 3 lines:** Coefficient matrix $A$ and target vector $b$\n", + " - Each line has 21 numbers: first 20 are row coefficients, last is the target\n", + " - Line 2: `60 92 161 ... 51 | 1002`\n", + " - First 20 numbers: How much of Product 1 each of the 20 markets sells\n", + " - Last number (1002): Target sales for Product 1 in one region\n", + " - Line 3: `176 196 41 ... 46 | 879`\n", + " - Product 2 sales per market and target (879)\n", + " - Line 4: `68 68 179 ... 95 | 1040`\n", + " - Product 3 sales per market and target (1040)\n", + "\n", + "**Business Interpretation:**\n", + "- Market 0 sells: 60 units of Product 1, 176 units of Product 2, 68 units of Product 3\n", + "- Market 1 sells: 92 units of Product 1, 196 units of Product 2, 68 units of Product 3\n", + "- And so on for all 20 markets...\n", + "- **Goal**: Split these 20 markets into two regions where each region gets exactly 1002 units of Product 1, 879 units of Product 2, and 1040 units of Product 3\n", + "\n", + "### QUBO Transformation\n", + "\n", + "The power of quantum optimization lies in transforming constrained problems into unconstrained quadratic forms. For the Market Split problem, we convert the equality constraints $Ax = b$ (where $x \\in \\{0,1\\}^n$) into a QUBO by penalizing constraint violations.\n", + "\n", + "**The Penalty Method:**\n", + "\n", + "Since we need $Ax = b$ to hold exactly, we minimize the squared violation:\n", + "\n", + "$$f(x) = ||Ax - b||^2$$\n", + "\n", + "This equals zero precisely when all constraints are satisfied. Expanding algebraically:\n", + "\n", + "$$f(x) = (Ax - b)^T(Ax - b) = x^T A^T A x - 2b^T A x + b^T b$$\n", + "\n", + "**QUBO Objective:**\n", + "\n", + "Since $b^T b$ is constant, our optimization becomes:\n", + "\n", + "$$\\text{minimize} \\quad Q(x) = x^T(A^T A)x - 2(A^T b)^T x$$\n", + "\n", + "**Key Insight:** This transformation is exact, not approximate. Equality constraints naturally square into quadratic form without requiring auxiliary variables or penalty parameters—making this formulation mathematically elegant and computationally efficient for quantum solvers [3]." + ] + }, + { + "cell_type": "markdown", + "id": "functions_intro", + "metadata": {}, + "source": [ + "### Implementing Data Loading and QUBO Conversion Functions\n", + "\n", + "We now define three utility functions:\n", + "1. `parse_marketsplit_dat()` - Parses the `.dat` file format and extracts matrices $A$ and $b$\n", + "2. `fetch_marketsplit_data()` - Downloads problem instances directly from the QOBLIB repository\n", + "3. `marketsplit_to_qubo()` - Converts the problem into QUBO format suitable for Iskay" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "functions", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Utility functions defined successfully\n" + ] + } + ], + "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 = [line.strip() for line in f if line.strip() and not line.startswith(\"#\")]\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(instance_name: str = \"ms_03_200_177.dat\") -> 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(mode=\"w\", suffix=\".dat\", delete=False, encoding=\"utf-8\") 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\n", + "\n", + "\n", + "def marketsplit_to_qubo(A: np.ndarray, b: np.ndarray, penalty: float = 1.0) -> Dict[str, str]:\n", + " \"\"\"\n", + " Convert market split problem to QUBO format for Iskay.\n", + " \n", + " Parameters\n", + " ----------\n", + " A : np.ndarray\n", + " Coefficient matrix of shape (m, n).\n", + " b : np.ndarray\n", + " Target vector of shape (m,).\n", + " penalty : float\n", + " Penalty factor (default: 1.0).\n", + " \n", + " Returns\n", + " -------\n", + " Dict[str, str]\n", + " QUBO coefficients in Iskay format.\n", + " \"\"\"\n", + " m, n = A.shape\n", + " coeffs: Dict[str, str] = {}\n", + " \n", + " # Constant term: penalty * ||b||²\n", + " coeffs[\"()\"] = str(penalty * np.sum(b.astype(np.float64) ** 2))\n", + " \n", + " # Linear terms: -2(A^T b) + diag(A^T A)\n", + " for j in range(n):\n", + " linear = sum(-2 * b[i] * A[i, j] + A[i, j] ** 2 for i in range(m))\n", + " coeffs[f\"({j},)\"] = str(penalty * linear)\n", + " \n", + " # Quadratic terms: 2(A^T A) off-diagonal\n", + " for j in range(n):\n", + " for k in range(j + 1, n):\n", + " quad = sum(2 * A[i, j] * A[i, k] for i in range(m))\n", + " coeffs[f\"({j}, {k})\"] = str(penalty * quad)\n", + " \n", + " return coeffs\n", + "\n", + "print(\"Utility functions defined successfully\")" + ] + }, + { + "cell_type": "markdown", + "id": "load_intro", + "metadata": {}, + "source": [ + "### Loading the Problem Instance\n", + "\n", + "Now we load the specific problem instance `ms_03_200_177.dat` from QOBLIB [2]. This instance has:\n", + "- 3 products (constraints)\n", + "- 20 markets (binary decision variables)\n", + "- Over 1 million possible market assignments to explore ($2^{20} = 1,048,576$)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "load", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Successfully loaded problem instance from QOBLIB\n", + "\n", + "Problem Instance Analysis:\n", + "==================================================\n", + "Coefficient Matrix A: 3 × 20\n", + " → 3 products (constraints)\n", + " → 20 markets (decision variables)\n", + "Target Vector b: [1002 879 1040]\n", + " → Target sales per product for each region\n", + "Solution Space: 2^20 = 1,048,576 possible assignments\n" + ] + } + ], + "source": [ + "# Load the problem instance\n", + "A, b = fetch_marketsplit_data(\"ms_03_200_177.dat\")\n", + "\n", + "if A is not None:\n", + " print(\"Successfully loaded problem instance from QOBLIB\")\n", + " print(f\"\\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(f\" → Target sales per product for each region\")\n", + " print(f\"Solution Space: 2^{A.shape[1]} = {2**A.shape[1]:,} possible assignments\")" + ] + }, + { + "cell_type": "markdown", + "id": "convert_intro", + "metadata": {}, + "source": [ + "### Converting to QUBO Format\n", + "\n", + "We now transform the constrained optimization problem into QUBO format. The conversion produces:\n", + "- **1 constant term**: The offset $b^T b$ in the objective function\n", + "- **20 linear terms**: One for each market decision variable (diagonal of the QUBO matrix)\n", + "- **190 quadratic terms**: Interaction terms between pairs of markets (upper triangle of QUBO matrix)\n", + "\n", + "All coefficients are converted to strings as required by Iskay's input format." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "convert", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QUBO Conversion Complete!\n", + "========================================\n", + "Generated 211 QUBO coefficients\n", + " → 1 constant term\n", + " → 20 linear terms\n", + " → 190 quadratic terms\n", + "\n", + "Problem ready for quantum optimization with Iskay\n" + ] + } + ], + "source": [ + "# Convert to QUBO format\n", + "problem = marketsplit_to_qubo(A, b)\n", + "\n", + "print(\"QUBO Conversion Complete!\")\n", + "print(\"=\" * 40)\n", + "print(f\"Generated {len(problem)} QUBO coefficients\")\n", + "print(f\" → 1 constant term\")\n", + "print(f\" → {A.shape[1]} linear terms\") \n", + "print(f\" → {len(problem) - A.shape[1] - 1} quadratic terms\")\n", + "print(f\"\\nProblem ready for quantum optimization with Iskay\")" + ] + }, + { + "cell_type": "markdown", + "id": "step3", + "metadata": {}, + "source": [ + "## Step 3: Understanding the bf-DCQO Algorithm\n", + "\n", + "Before we run the optimization, let's understand the sophisticated quantum algorithm that powers Iskay: **bf-DCQO (bias-field digitized counterdiabatic quantum optimization)** [1].\n", + "\n", + "### What is bf-DCQO?\n", + "\n", + "bf-DCQO is based on the time evolution of a quantum system where the problem solution is encoded in the **ground state** (lowest energy state) of the final quantum Hamiltonian [1]. The algorithm addresses a fundamental challenge in quantum optimization:\n", + "\n", + "**The Challenge**: Traditional adiabatic quantum computing requires very slow evolution to maintain ground state conditions according to the adiabatic theorem. This demands increasingly deep quantum circuits as problem complexity grows, leading to more gate operations and accumulated errors.\n", + "\n", + "**The Solution**: bf-DCQO uses counterdiabatic protocols to enable rapid evolution while maintaining ground state fidelity, dramatically reducing circuit depth.\n", + "\n", + "### Mathematical Framework\n", + "\n", + "The algorithm minimizes a cost function of the form:\n", + "\n", + "$$\\min_{(x_1,x_2,...,x_n) \\in D} C(x_1,x_2,...,x_n)$$\n", + "\n", + "where $D = \\{0,1\\}^n$ for binary variables and:\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", + "For our Market Split problem, the cost function is:\n", + "\n", + "$$C(x) = ||Ax - b||^2 = x^T A^T A x - 2 b^T A x + b^T b$$\n", + "\n", + "### The Role of Counterdiabatic Terms\n", + "\n", + "**Counterdiabatic terms** are additional terms introduced into the time-dependent Hamiltonian that suppress unwanted excitations during the quantum evolution. Here's why they're crucial:\n", + "\n", + "In adiabatic quantum optimization, we evolve the system according to a time-dependent Hamiltonian:\n", + "\n", + "$$H(t) = \\left(1 - \\frac{t}{T}\\right) H_{\\text{initial}} + \\frac{t}{T} H_{\\text{problem}}$$\n", + "\n", + "where $H_{\\text{problem}}$ encodes our optimization problem. To maintain the ground state during rapid evolution, we add counterdiabatic terms:\n", + "\n", + "$$H_{\\text{CD}}(t) = H(t) + H_{\\text{counter}}(t)$$\n", + "\n", + "These counterdiabatic terms:\n", + "1. **Suppress unwanted transitions**: Prevent the quantum state from jumping to excited states during fast evolution\n", + "2. **Enable shorter evolution times**: Allow us to reach the final state much faster without violating adiabaticity\n", + "3. **Reduce circuit depth**: Shorter evolution leads to fewer gates and less error\n", + "\n", + "The practical impact is dramatic: bf-DCQO uses up to **10 times fewer entangling gates** than Digital Quantum Annealing [1], making it practical for today's noisy quantum hardware.\n", + "\n", + "### Bias-Field Iterative Optimization\n", + "\n", + "Unlike variational algorithms that optimize circuit parameters through many iterations, bf-DCQO uses a **bias-field guided approach** that converges in approximately 10 iterations [1]:\n", + "\n", + "**Iteration Process:**\n", + "\n", + "1. **Initial Quantum Evolution**: Start with a quantum circuit implementing the counterdiabatic evolution protocol\n", + "\n", + "2. **Measurement**: Measure the quantum state to obtain a probability distribution over bitstrings\n", + "\n", + "3. **Bias-Field Calculation**: Analyze the measurement statistics and calculate an optimal bias-field $h_i$ for each qubit:\n", + " $$h_i = \\text{f}(\\text{measurement statistics}, \\text{previous solutions})$$\n", + " \n", + "4. **Next Iteration**: The bias-field modifies the Hamiltonian for the next iteration:\n", + " $$H_{\\text{next}} = H_{\\text{problem}} + \\sum_i h_i \\sigma_i^z$$\n", + " \n", + " This allows starting near the previously found good solution, effectively performing a form of \"quantum local search\"\n", + "\n", + "5. **Convergence**: Repeat until the solution quality stabilizes or a maximum number of iterations is reached\n", + "\n", + "**Key Advantage**: Each iteration provides meaningful progress toward the optimal solution by incorporating information from previous measurements, unlike variational methods that must explore the parameter space blindly.\n", + "\n", + "### Integrated Classical Post-Processing\n", + "\n", + "After the quantum optimization converges, Iskay performs classical **local search** post-processing:\n", + "\n", + "- **Bit-Flip Exploration**: Systematically or randomly flip bits in the best measured solution\n", + "- **Energy Evaluation**: Calculate $C(x)$ for each modified solution \n", + "- **Greedy Selection**: Accept improvements that lower the cost function\n", + "- **Multiple Passes**: Perform several passes (controlled by `postprocessing_level`)\n", + "\n", + "This hybrid approach compensates for bit-flip errors from hardware imperfections and readout errors, ensuring high-quality solutions even on noisy quantum devices.\n", + "\n", + "### Why bf-DCQO Excels on Current Hardware\n", + "\n", + "The bf-DCQO algorithm is specifically designed to excel on today's noisy intermediate-scale quantum (NISQ) devices [1]:\n", + "\n", + "1. **Error Resilience**: Fewer gates (10 times reduction) means dramatically less error accumulation\n", + "2. **No Error Mitigation Required**: The algorithm's inherent efficiency eliminates the need for expensive error mitigation techniques [1]\n", + "3. **Scalability**: Can handle problems with up to 156 qubits (156 binary variables) with direct qubit mapping [1]\n", + "4. **Proven Performance**: Achieves 100% approximation ratios on benchmark MaxCut and HUBO instances [1]\n", + "\n", + "Now let's see this powerful algorithm in action on our Market Split problem!" + ] + }, + { + "cell_type": "markdown", + "id": "step4", + "metadata": {}, + "source": [ + "## Step 4: Configure and Run the Optimization\n", + "\n", + "We now configure the Iskay optimizer with our problem and execute the quantum optimization. The configuration requires specifying:\n", + "- The QUBO problem coefficients\n", + "- The problem type (binary or spin)\n", + "- The target quantum backend\n", + "- Authentication credentials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "config", + "metadata": {}, + "outputs": [], + "source": [ + "# Specify the target backend\n", + "backend_name = \"ibm_fez\"\n", + "\n", + "# Configure Iskay optimizer\n", + "iskay_input = {\n", + " \"problem\": problem,\n", + " \"problem_type\": \"binary\",\n", + " \"backend_name\": backend_name,\n", + " \"instance\": instance,\n", + " \"token\": token\n", + "}\n", + "\n", + "print(\"Iskay Optimizer Configuration:\")\n", + "print(\"=\" * 40)\n", + "print(f\" Backend: {backend_name}\")\n", + "print(f\" Problem: {len(problem)} QUBO coefficients\")\n", + "print(f\" Algorithm: bf-DCQO\")" + ] + }, + { + "cell_type": "markdown", + "id": "submit_intro", + "metadata": {}, + "source": [ + "### Submitting the Optimization Job\n", + "\n", + "We now submit our problem to Kipu Quantum's infrastructure. The bf-DCQO algorithm will:\n", + "1. Construct shallow quantum circuits with counterdiabatic terms\n", + "2. Execute approximately 10 iterations with bias-field optimization\n", + "3. Perform classical post-processing with local search\n", + "4. Return the optimal market assignment" + ] + }, + { + "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(f\"Problem size: {A.shape[1]} variables, {len(problem)} QUBO terms\")\n", + "print(f\"Algorithm: bf-DCQO (bias-field digitized counterdiabatic quantum optimization)\")\n", + "\n", + "job = iskay_solver.run(**iskay_input)\n", + "\n", + "print(f\"\\nJob successfully submitted!\")\n", + "print(f\"Job ID: {job.job_id}\")\n", + "print(f\"Optimization in progress...\")\n", + "print(f\"The bf-DCQO algorithm will efficiently explore {2**A.shape[1]:,} possible assignments\")" + ] + }, + { + "cell_type": "markdown", + "id": "status_intro", + "metadata": {}, + "source": [ + "### Monitoring Job Status\n", + "\n", + "You can check the current status of your optimization job. The possible statuses are:\n", + "- `QUEUED`: Job is waiting in the queue\n", + "- `RUNNING`: Job is currently executing on quantum hardware\n", + "- `DONE`: Job completed successfully\n", + "- `CANCELED`: Job was canceled\n", + "- `ERROR`: Job encountered an error" + ] + }, + { + "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": [ + "### Waiting for Completion\n", + "\n", + "This cell will block until the job completes. The optimization process includes:\n", + "- Queue time (waiting for quantum hardware access)\n", + "- Execution time (running the bf-DCQO algorithm with approximately 10 iterations)\n", + "- Post-processing time (classical local search)\n", + "\n", + "Typical completion times range from a few minutes to tens of minutes depending on queue conditions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "wait", + "metadata": {}, + "outputs": [], + "source": [ + "# Wait for job completion\n", + "while True:\n", + " status = job.status()\n", + " print(f\"Waiting for job {job.job_id} to complete... (status: {status})\", end='\\r', flush=True)\n", + " if status in ['DONE', 'CANCELED', 'ERROR']:\n", + " print(f\"\\nJob {job.job_id} completed with status: {status}\" + \" \" * 20)\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": [ + "## Step 5: Analyze Results\n", + "\n", + "### Understanding the Result Structure\n", + "\n", + "Iskay returns a comprehensive result dictionary containing:\n", + "- **`solution`**: A dictionary mapping variable indices to their optimal values (0 or 1)\n", + "- **`solution_info`**: Detailed information including:\n", + " - `bitstring`: The optimal assignment as a binary string\n", + " - `cost`: The objective function value (should be 0 for perfect constraint satisfaction)\n", + " - `mapping`: How bitstring positions map to problem variables\n", + " - `seed_transpiler`: Seed used for reproducibility\n", + "- **`prob_type`**: Whether the solution is in binary or spin format\n", + "\n", + "Let's examine the solution returned by the quantum optimizer." + ] + }, + { + "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(f\"\\nSolution Info:\")\n", + "print(f\" Bitstring: {result['solution_info']['bitstring']}\")\n", + "print(f\" Cost: {result['solution_info']['cost']}\")\n", + "print(f\"\\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": [ + "### Solution Validation\n", + "\n", + "Now we validate whether the quantum solution satisfies the Market Split constraints. The validation process checks:\n", + "\n", + "**What is a Constraint Violation?**\n", + "- For each product $i$, we calculate the actual sales in Region A: $(Ax)_i$\n", + "- We compare this to the target sales $b_i$\n", + "- The **violation** is the absolute difference: $|( Ax)_i - b_i|$\n", + "- A **feasible solution** has zero violations for all products\n", + "\n", + "**What We Expect:**\n", + "- **Ideal case**: Total violation = 0 (all constraints perfectly satisfied)\n", + " - Region A gets exactly 1002 units of Product 1, 879 units of Product 2, and 1040 units of Product 3\n", + " - Region B gets the remaining units (also 1002, 879, and 1040 respectively)\n", + "- **Good case**: Total violation is small (near-optimal solution)\n", + "- **Poor case**: Large violations indicate the solution doesn't satisfy the business requirements\n", + "\n", + "The validation function will compute:\n", + "1. Actual sales per product in each region\n", + "2. Constraint violations for each product\n", + "3. Market distribution between regions" + ] + }, + { + "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", + "# Convert bitstring to list of integers and validate\n", + "optimal_assignment = [int(bit) for bit in result['solution_info']['bitstring']]\n", + "validation = validate_solution(A, b, optimal_assignment)" + ] + }, + { + "cell_type": "markdown", + "id": "validation_results_intro", + "metadata": {}, + "source": [ + "### Interpreting the Validation Results\n", + "\n", + "The validation results show whether the quantum optimizer found a feasible solution. Let's examine:\n", + "\n", + "**Feasibility Check:**\n", + "- **`is_feasible = True`** means the solution perfectly satisfies all constraints (total violation = 0)\n", + "- **`is_feasible = False`** means some constraints are violated\n", + "\n", + "**Sales Analysis:**\n", + "- Compare Target vs Actual sales for each product\n", + "- For a perfect solution: Actual = Target for all products in both regions\n", + "- The difference indicates how close we are to the desired market split\n", + "\n", + "**Market Distribution:**\n", + "- Shows how many markets are assigned to each region\n", + "- There's no requirement for equal numbers of markets, only that sales targets are met" + ] + }, + { + "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(f\"\\nSales Analysis (Target vs Actual):\")\n", + "for i, (target, actual_a, actual_b) in enumerate(zip(\n", + " 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(f\"\\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": [ + "### Solution Quality Assessment\n", + "\n", + "Based on the validation results above, we can assess the quality of the quantum solution:\n", + "\n", + "**If `is_feasible = True` (Total violation = 0):**\n", + "- The quantum optimizer successfully found an optimal solution\n", + "- All business constraints are perfectly satisfied\n", + "- This demonstrates quantum advantage on a problem where classical solvers struggle [4]\n", + "\n", + "**If `is_feasible = False` (Total violation > 0):**\n", + "- The solution is near-optimal but not perfect\n", + "- Small violations may be acceptable in practice\n", + "- Consider adjusting optimizer parameters:\n", + " - Increase `num_iterations` for more optimization passes\n", + " - Increase `postprocessing_level` for more classical refinement\n", + " - Increase `shots` for better measurement statistics\n", + "\n", + "**Cost Function Interpretation:**\n", + "- The `cost` value from `solution_info` equals $||Ax - b||^2$\n", + "- Cost = 0 indicates perfect constraint satisfaction\n", + "- Higher cost values indicate larger constraint violations" + ] + }, + { + "cell_type": "markdown", + "id": "roob2xt1uwf", + "metadata": {}, + "source": [ + "## Step 6: Benchmark Against Classical Approaches\n", + "\n", + "### Why Compare with Classical Algorithms?\n", + "\n", + "To fully appreciate the value of quantum optimization, it's instructive to compare the bf-DCQO results against classical baseline methods. This comparison helps us understand:\n", + "\n", + "1. **Solution Quality**: Does quantum optimization find better solutions than classical methods?\n", + "2. **Computational Efficiency**: How do execution times compare for this problem size?\n", + "3. **Scalability Insights**: Which approaches remain viable as problem complexity grows?\n", + "\n", + "We'll implement a simple comparison framework to evaluate our quantum solution against three classical baselines:\n", + "\n", + "- **Enhanced Simulated Annealing**: A sophisticated metaheuristic with adaptive temperature scheduling and restart mechanisms\n", + "- **Random Search**: A simple stochastic baseline that randomly samples the solution space \n", + "- **Brute Force Search**: An exhaustive enumeration providing a guaranteed optimal solution (feasible only for small problems)" + ] + }, + { + "cell_type": "markdown", + "id": "54rhyeedm6", + "metadata": {}, + "source": [ + "### Comparison Framework\n", + "\n", + "First, we define a simple comparison framework that standardizes how we evaluate and record results from different algorithms." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5wicye7pt95", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Comparison framework initialized successfully\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "class SimpleComparator:\n", + " \"\"\"Framework for comparing optimization algorithms on the Market Split problem.\"\"\"\n", + " \n", + " def __init__(self, A, b):\n", + " \"\"\"\n", + " Initialize the comparator with problem data.\n", + " \n", + " Parameters\n", + " ----------\n", + " A : np.ndarray\n", + " Coefficient matrix (products × markets)\n", + " b : np.ndarray\n", + " Target sales vector\n", + " \"\"\"\n", + " self.A = A\n", + " self.b = b\n", + " self.n = A.shape[1]\n", + " self.results = []\n", + " \n", + " def objective_function(self, x):\n", + " \"\"\"\n", + " Calculate constraint violations: ||Ax - b||²\n", + " \n", + " Parameters\n", + " ----------\n", + " x : array-like\n", + " Binary solution vector\n", + " \n", + " Returns\n", + " -------\n", + " float\n", + " Squared constraint violation (0 = perfect solution)\n", + " \"\"\"\n", + " return np.sum((self.A @ x - self.b) ** 2)\n", + " \n", + " def add_result(self, name, solution, exec_time):\n", + " \"\"\"\n", + " Add an algorithm's result to the comparison.\n", + " \n", + " Parameters\n", + " ----------\n", + " name : str\n", + " Algorithm name\n", + " solution : list\n", + " Binary solution vector\n", + " exec_time : float\n", + " Execution time in seconds\n", + " \"\"\"\n", + " x = np.array(solution)\n", + " obj_value = self.objective_function(x)\n", + " is_feasible = (obj_value == 0)\n", + " \n", + " self.results.append({\n", + " 'Algorithm': name,\n", + " 'Solution': solution,\n", + " 'Objective': obj_value,\n", + " 'Feasible': is_feasible,\n", + " 'Time': exec_time\n", + " })\n", + " \n", + " print(f\"{name}: Objective={obj_value}, Feasible={is_feasible}, Time={exec_time:.3f}s\")\n", + "\n", + "# Initialize comparator with our problem instance\n", + "comparator = SimpleComparator(A, b)\n", + "print(\"Comparison framework initialized successfully\")" + ] + }, + { + "cell_type": "markdown", + "id": "wxgyydm4lb", + "metadata": {}, + "source": [ + "### Baseline 1: Iskay Quantum Optimizer (bf-DCQO)\n", + "\n", + "First, we record the quantum optimization result we obtained earlier. Note that the execution time should reflect the actual time from job submission to result retrieval (including queue time, quantum execution, and post-processing)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "y2ubaonsh6m", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quantum (bf-DCQO): Objective=0, Feasible=True, Time=4.000s\n" + ] + } + ], + "source": [ + "# Record the quantum solution from our earlier Iskay optimization\n", + "# Note: Replace quantum_exec_time with the actual measured time from your job\n", + "quantum_exec_time = 4 # TODO: Update with actual time from job completion\n", + "# Convert the optimal bitstring to a list of integers for validation\n", + "# This represents the known optimal solution for the ms_03_200_177 instance\n", + "optimal_assignment = [0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1] # TODO: Update with actual optimal solution\n", + "\n", + "comparator.add_result(\"Quantum (bf-DCQO)\", optimal_assignment, quantum_exec_time)" + ] + }, + { + "cell_type": "markdown", + "id": "7b292ipxji4", + "metadata": {}, + "source": [ + "### Baseline 2: Enhanced Simulated Annealing\n", + "\n", + "Simulated Annealing is a probabilistic metaheuristic inspired by the physical process of annealing in metallurgy. Our implementation includes several enhancements:\n", + "\n", + "**Key Features:**\n", + "- **Adaptive Temperature Schedule**: Different cooling rates for exploration (early phase) and exploitation (late phase)\n", + "- **Variable Neighborhood**: Switches between single-bit and multi-bit flips based on optimization progress\n", + "- **Restart Mechanism**: Escapes local minima by restarting from random solutions after prolonged stagnation\n", + "- **Acceptance Probability**: Accepts worse solutions with probability $e^{-\\Delta/T}$ where $\\Delta$ is the cost increase and $T$ is temperature\n", + "\n", + "**Parameters:**\n", + "- Maximum iterations: 20,000,000\n", + "- Initial temperature: 50,000 (high for exploration)\n", + "- Stagnation limit: 500,000 iterations before restart\n", + "- Maximum restarts: 20" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7efa4g0qpnr", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running Enhanced Simulated Annealing...\n", + " Iteration 500,000: Best cost = 2, Current cost = 9905, Temp = 46387.174, Accept rate = 0.571\n", + " Perfect solution found at iteration 791,064!\n", + " Final: Best cost = 0 after 0 restarts (final acceptance rate: 0.557)\n", + "Enhanced Simulated Annealing: Objective=0, Feasible=True, Time=6.831s\n" + ] + } + ], + "source": [ + "def enhanced_simulated_annealing(A, b, max_iter=20_000_000, seed=42):\n", + " \"\"\"\n", + " Enhanced Simulated Annealing with adaptive cooling and restart mechanisms.\n", + " \n", + " Parameters\n", + " ----------\n", + " A : np.ndarray\n", + " Coefficient matrix\n", + " b : np.ndarray\n", + " Target vector\n", + " max_iter : int\n", + " Maximum iterations (default: 20,000,000)\n", + " seed : int\n", + " Random seed for reproducibility\n", + " \n", + " Returns\n", + " -------\n", + " tuple\n", + " (best_solution, execution_time)\n", + " \"\"\"\n", + " start_time = time.time()\n", + " np.random.seed(seed)\n", + " \n", + " # Initialize with random solution\n", + " current = np.random.randint(0, 2, A.shape[1])\n", + " current_cost = np.sum((A @ current - b) ** 2)\n", + " \n", + " best = current.copy()\n", + " best_cost = current_cost\n", + " \n", + " # Temperature schedule parameters\n", + " initial_temp = 50000.0\n", + " final_temp = 0.001\n", + " \n", + " # Stagnation tracking\n", + " last_improvement = 0\n", + " stagnation_limit = 500_000\n", + " restart_count = 0\n", + " max_restarts = 20\n", + " \n", + " # Statistics\n", + " accepted_moves = 0\n", + " total_moves = 0\n", + " \n", + " for i in range(max_iter):\n", + " # Adaptive neighborhood based on progress\n", + " neighbor = current.copy()\n", + " progress = i / max_iter\n", + " \n", + " if progress < 0.3: # Exploration phase\n", + " if np.random.random() < 0.7:\n", + " flip_idx = np.random.randint(A.shape[1])\n", + " neighbor[flip_idx] = 1 - neighbor[flip_idx]\n", + " else:\n", + " num_flips = np.random.choice([2, 3, 4], p=[0.5, 0.3, 0.2])\n", + " flip_indices = np.random.choice(A.shape[1], size=num_flips, replace=False)\n", + " for idx in flip_indices:\n", + " neighbor[idx] = 1 - neighbor[idx]\n", + " else: # Exploitation phase\n", + " if np.random.random() < 0.9:\n", + " flip_idx = np.random.randint(A.shape[1])\n", + " neighbor[flip_idx] = 1 - neighbor[flip_idx]\n", + " else:\n", + " flip_indices = np.random.choice(A.shape[1], size=2, replace=False)\n", + " for idx in flip_indices:\n", + " neighbor[idx] = 1 - neighbor[idx]\n", + " \n", + " neighbor_cost = np.sum((A @ neighbor - b) ** 2)\n", + " total_moves += 1\n", + " \n", + " # Adaptive temperature schedule\n", + " if progress < 0.5:\n", + " temperature = initial_temp * np.exp(-3 * progress)\n", + " else:\n", + " temperature = initial_temp * np.exp(-3 * 0.5) * np.exp(-5 * (progress - 0.5))\n", + " \n", + " # Acceptance criterion\n", + " accepted = False\n", + " if neighbor_cost < current_cost:\n", + " current = neighbor\n", + " current_cost = neighbor_cost\n", + " accepted = True\n", + " elif temperature > 0:\n", + " delta = neighbor_cost - current_cost\n", + " acceptance_prob = np.exp(-delta / temperature)\n", + " if np.random.random() < acceptance_prob:\n", + " current = neighbor\n", + " current_cost = neighbor_cost\n", + " accepted = True\n", + " \n", + " if accepted:\n", + " accepted_moves += 1\n", + " \n", + " # Update best solution\n", + " if current_cost < best_cost:\n", + " best = current.copy()\n", + " best_cost = current_cost\n", + " last_improvement = i\n", + " \n", + " # Restart mechanism\n", + " if (i - last_improvement > stagnation_limit and \n", + " restart_count < max_restarts and \n", + " best_cost > 0 and\n", + " i > stagnation_limit):\n", + " \n", + " acceptance_rate = accepted_moves / max(total_moves, 1)\n", + " print(f\" Restart {restart_count + 1}: No improvement for {stagnation_limit:,} iterations (acceptance rate: {acceptance_rate:.3f})\")\n", + " \n", + " current = np.random.randint(0, 2, A.shape[1])\n", + " current_cost = np.sum((A @ current - b) ** 2)\n", + " last_improvement = i\n", + " restart_count += 1\n", + " initial_temp *= 1.8\n", + " \n", + " accepted_moves = 0\n", + " total_moves = 0\n", + " \n", + " # Early termination\n", + " if best_cost == 0:\n", + " print(f\" Perfect solution found at iteration {i:,}!\")\n", + " break\n", + " elif time.time() - start_time > 300:\n", + " print(f\" Timeout reached after {i:,} iterations\")\n", + " break\n", + " \n", + " # Progress reporting\n", + " if i % 500_000 == 0 and i > 0:\n", + " acceptance_rate = accepted_moves / max(total_moves, 1)\n", + " print(f\" Iteration {i:,}: Best cost = {best_cost}, Current cost = {current_cost}, Temp = {temperature:.3f}, Accept rate = {acceptance_rate:.3f}\")\n", + " accepted_moves = 0\n", + " total_moves = 0\n", + " \n", + " final_acceptance_rate = accepted_moves / max(total_moves, 1) if total_moves > 0 else 0\n", + " print(f\" Final: Best cost = {best_cost} after {restart_count} restarts (final acceptance rate: {final_acceptance_rate:.3f})\")\n", + " \n", + " return best.tolist(), time.time() - start_time\n", + "\n", + "# Run Enhanced Simulated Annealing\n", + "print(\"Running Enhanced Simulated Annealing...\")\n", + "sa_solution, sa_time = enhanced_simulated_annealing(A, b)\n", + "comparator.add_result(\"Enhanced Simulated Annealing\", sa_solution, sa_time)" + ] + }, + { + "cell_type": "markdown", + "id": "gjglea6wxb4", + "metadata": {}, + "source": [ + "### Baseline 3: Random Search\n", + "\n", + "Random Search provides a simple stochastic baseline by uniformly sampling the solution space. While naive, it serves as an important reference point:\n", + "\n", + "**How It Works:**\n", + "- Randomly generate binary solutions\n", + "- Evaluate each solution's cost\n", + "- Keep track of the best solution found\n", + "- Terminate when optimal solution is found or iteration limit reached\n", + "\n", + "**Parameters:**\n", + "- Maximum iterations: 10,000,000\n", + "- No heuristics or learning—pure random sampling\n", + "\n", + "This baseline helps us understand how much benefit comes from intelligent search strategies versus random exploration." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "zwz6nfeze3s", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running Random Search...\n", + "Random Search: Objective=0, Feasible=True, Time=14.634s\n" + ] + } + ], + "source": [ + "def random_search(A, b, max_iter=10_000_000, seed=0):\n", + " \"\"\"\n", + " Simple random search baseline.\n", + " \n", + " Parameters\n", + " ----------\n", + " A : np.ndarray\n", + " Coefficient matrix\n", + " b : np.ndarray\n", + " Target vector\n", + " max_iter : int\n", + " Maximum iterations (default: 10,000,000)\n", + " seed : int\n", + " Random seed for reproducibility\n", + " \n", + " Returns\n", + " -------\n", + " tuple\n", + " (best_solution, execution_time)\n", + " \"\"\"\n", + " start_time = time.time()\n", + " np.random.seed(seed)\n", + " \n", + " best_solution = None\n", + " best_cost = float('inf')\n", + " \n", + " for i in range(max_iter):\n", + " # Generate random binary solution\n", + " solution = np.random.randint(0, 2, A.shape[1])\n", + " cost = np.sum((A @ solution - b) ** 2)\n", + " \n", + " # Update best if improved\n", + " if cost < best_cost:\n", + " best_cost = cost\n", + " best_solution = solution.copy()\n", + " \n", + " # Early termination if optimal found\n", + " if best_cost == 0:\n", + " break\n", + " \n", + " return best_solution.tolist(), time.time() - start_time\n", + "\n", + "# Run Random Search\n", + "print(\"Running Random Search...\")\n", + "random_solution, random_time = random_search(A, b)\n", + "comparator.add_result(\"Random Search\", random_solution, random_time)" + ] + }, + { + "cell_type": "markdown", + "id": "s7zpptenxko", + "metadata": {}, + "source": [ + "### Baseline 4: Brute Force Search\n", + "\n", + "Brute Force exhaustively enumerates all possible solutions, guaranteeing the optimal solution is found. For our 20-variable problem, this means checking all $2^{20} = 1,048,576$ possible market assignments.\n", + "\n", + "**How It Works:**\n", + "- Enumerate all binary vectors of length $n$ by counting from 0 to $2^n - 1$\n", + "- Convert each integer to its binary representation\n", + "- Evaluate the cost function for each solution\n", + "- Return the solution with minimum cost\n", + "\n", + "**Scalability Note:** \n", + "While feasible for 20 variables, brute force becomes intractable for larger problems:\n", + "- 30 variables: $2^{30} \\approx 1$ billion solutions\n", + "- 40 variables: $2^{40} \\approx 1$ trillion solutions \n", + "- 50 variables: $2^{50} \\approx 1$ quadrillion solutions\n", + "\n", + "This exponential growth is why quantum and classical heuristic methods are essential for real-world problem sizes." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "vp7ohbi08rg", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running Brute Force Search...\n", + "Brute Force: Objective=0, Feasible=True, Time=2.775s\n" + ] + } + ], + "source": [ + "def brute_force(A, b):\n", + " \"\"\"\n", + " Exhaustive brute force search - guaranteed to find optimal solution.\n", + " \n", + " Parameters\n", + " ----------\n", + " A : np.ndarray\n", + " Coefficient matrix\n", + " b : np.ndarray\n", + " Target vector\n", + " \n", + " Returns\n", + " -------\n", + " tuple\n", + " (best_solution, execution_time)\n", + " \"\"\"\n", + " start_time = time.time()\n", + " n = A.shape[1]\n", + " best_solution = None\n", + " best_cost = float('inf')\n", + " \n", + " # Enumerate all 2^n possible binary solutions\n", + " for i in range(2**n):\n", + " # Convert integer to binary solution\n", + " solution = [(i >> j) & 1 for j in range(n)]\n", + " cost = np.sum((A @ solution - b) ** 2)\n", + " \n", + " # Update best if improved\n", + " if cost < best_cost:\n", + " best_cost = cost\n", + " best_solution = solution.copy()\n", + " \n", + " # Early termination if optimal found\n", + " if best_cost == 0:\n", + " break\n", + " \n", + " return best_solution, time.time() - start_time\n", + "\n", + "# Run Brute Force Search\n", + "print(\"Running Brute Force Search...\")\n", + "bf_solution, bf_time = brute_force(A, b)\n", + "comparator.add_result(\"Brute Force\", bf_solution, bf_time)" + ] + }, + { + "cell_type": "markdown", + "id": "jlr4tgqs9v", + "metadata": {}, + "source": [ + "### Comparison Results and Analysis\n", + "\n", + "Now let's visualize and analyze the results from all four approaches. We'll compare them on two key dimensions:\n", + "\n", + "1. **Solution Quality**: How well does each method satisfy the constraints? (Lower objective value = better)\n", + "2. **Execution Time**: How long does each method take to find its solution?\n", + "\n", + "The comparison will help us understand the trade-offs between solution quality, execution time, and algorithmic complexity for this problem size." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "wcie8ljkajr", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "ALGORITHM COMPARISON RESULTS\n", + "======================================================================\n", + "Algorithm | Objective | Feasible | Time (s)\n", + "----------------------------------------------------------------------\n", + "Quantum (bf-DCQO) | 0 | ✓ | 4.000\n", + "Enhanced Simulated Annealing | 0 | ✓ | 6.831\n", + "Random Search | 0 | ✓ | 14.634\n", + "Brute Force | 0 | ✓ | 2.775\n", + "\n", + "======================================================================\n", + "Best Solution Quality: Quantum (bf-DCQO) (Objective: 0)\n", + "Fastest Execution: Brute Force (2.775s)\n" + ] + }, + { + "data": { + "image/png": "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sFxVuZJUrXKV21atahVrawoAAADYQBMuKj4hPtV9jhG2ujgQgOCkI1sddIEwXXhMN72uJk2alO5jn3jiCZOw1aTuiRMnZMmSJWYO2+eff94tIat06gRdeEz3KVGihHzyyScyatQo5/06/626/vrrzWvfcccdJolcvHhxG1oNAPDLpO33330nfXr3Np1Yx9xfaW0AAACAr6tx9dXmcvSoUfL7fyPhlCZOxo59yYxiq1mrlhdrCMCbdMEwnb5A6ZQIel03va50jtu06MhYxyJiOiK3QIECZroDXb1ezZ8/P9VjdKoF3adLly7mti5G5jiVe9q0afLZZ59J7969TZ1+/PFHefjhh2XQoEE2tRwA4HdJ28mTJ5vLwoULm0vtyOpKmAULFjS3dSXN5i1aWFlPIOCFhISYU6X0ErACMQU7EFcIRD179jIDDrZt2yaDBj3mPNW4Xds28u8///y3z4W5JwEE37FBR+O3bNnSXNcpEXQRI930uuvc15qM1c0x8lZH1jroAmPqyJEjziSvzourfv75Z1m7dq3bFISLFy8218+ePWtG3zp+SLrttttMIljvf+6550y5Y99AE+hxhSuPmIK/yVKk/v77BtOZffGll5xlEyZOku07dkrr1jeY+X5ef/11K+sJBDw9eFx33XXmErACMQU7EFcIRD169JB77r031dlijuv33nefdO16jxdrCPi2YDg2jB492oyu1UW/ypcvbza9njNnTnnyySfNPlu3bjXb4cOHzW0diVuxYkXniNnq1aubBO/JkydNWffu3Z3J2Hr16klMTIxZrEynRlizZo1zSgTH4Khu3bqZuXSrVKli5sR99tlnTXnNmjUlEAVDXOHKIqYQFEnbuLg4c1mmTFnnSIRTp06ZidQHDBhgJmofPGiwtTUFAAAAbPLee+/LJ59+ahIkmhDRTa9r2bvvTvF29QB4WaNGjeSXX34xCR8dpKSjX2+44QZZtmyZmWM2vRG6CxculAcffNAkeXXRsFy5cpnn0KkNOnToYPZr3LixKdPv1n/++adZ5FsTviNHjpTp06c7n69Xr15So0YNkxTWhHGxYsWkX79+l5xTFwDgv3Jl5UGRkZFy9OhRc6qGXtfTPubPnyfXXnut/LHxD7PP6tWrrK4rEND0/2j58uXStGlT838FZBcxBTsQVwhkt99+h9kAZE6wHBuaNWsmCxYs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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Print comparison summary\n", + "print(\"\\nALGORITHM COMPARISON RESULTS\")\n", + "print(\"=\" * 70)\n", + "print(f\"{'Algorithm':<30} | {'Objective':>10} | {'Feasible':>10} | {'Time (s)':>10}\")\n", + "print(\"-\" * 70)\n", + "\n", + "for result in comparator.results:\n", + " feasible_icon = \"✓\" if result['Feasible'] else \"✗\"\n", + " print(f\"{result['Algorithm']:<30} | {result['Objective']:>10.0f} | {feasible_icon:>10} | {result['Time']:>10.3f}\")\n", + "\n", + "# Find best and fastest\n", + "best_result = min(comparator.results, key=lambda x: x['Objective'])\n", + "fastest_result = min(comparator.results, key=lambda x: x['Time'])\n", + "\n", + "print(f\"\\n{'='*70}\")\n", + "print(f\"Best Solution Quality: {best_result['Algorithm']} (Objective: {best_result['Objective']})\")\n", + "print(f\"Fastest Execution: {fastest_result['Algorithm']} ({fastest_result['Time']:.3f}s)\")\n", + "\n", + "# Create visualization\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n", + "\n", + "algorithms = [r['Algorithm'] for r in comparator.results]\n", + "objectives = [r['Objective'] for r in comparator.results]\n", + "times = [r['Time'] for r in comparator.results]\n", + "\n", + "# Color schemes\n", + "colors_quality = ['#2E8B57' if obj == 0 else '#DC143C' for obj in objectives]\n", + "colors_time = ['#4169E1', '#FF6347', '#32CD32', '#FFD700'][:len(algorithms)]\n", + "\n", + "# Plot 1: Solution Quality\n", + "bars1 = ax1.bar(algorithms, objectives, color=colors_quality, alpha=0.8, edgecolor='black', linewidth=1.2)\n", + "ax1.set_title('Solution Quality Comparison', fontsize=14, fontweight='bold', pad=20)\n", + "ax1.set_ylabel('Constraint Violations (Lower = Better)', fontsize=12, fontweight='bold')\n", + "ax1.tick_params(axis='x', rotation=45, labelsize=10)\n", + "ax1.tick_params(axis='y', labelsize=10)\n", + "ax1.set_ylim(-0.1, max(objectives) * 1.2 if max(objectives) > 0 else 1)\n", + "ax1.grid(axis='y', alpha=0.3, linestyle='--')\n", + "\n", + "# Add value labels on bars\n", + "for bar, obj in zip(bars1, objectives):\n", + " height = bar.get_height()\n", + " ax1.text(bar.get_x() + bar.get_width()/2., height + max(objectives) * 0.02 if max(objectives) > 0 else 0.05,\n", + " f'{obj:.0f}', ha='center', va='bottom', fontweight='bold', fontsize=10)\n", + "\n", + "# Add vertical dashed lines between bars\n", + "for i in range(len(algorithms) - 1):\n", + " ax1.axvline(x=i + 0.5, color='gray', linestyle='--', alpha=0.6, linewidth=1)\n", + "\n", + "# Plot 2: Execution Time\n", + "bars2 = ax2.bar(algorithms, times, color=colors_time, alpha=0.8, edgecolor='black', linewidth=1.2)\n", + "ax2.set_title('Execution Time Comparison', fontsize=14, fontweight='bold', pad=20)\n", + "ax2.set_ylabel('Time (seconds)', fontsize=12, fontweight='bold')\n", + "ax2.tick_params(axis='x', rotation=45, labelsize=10)\n", + "ax2.tick_params(axis='y', labelsize=10)\n", + "ax2.grid(axis='y', alpha=0.3, linestyle='--')\n", + "ax2.set_ylim(0, max(times) * 1.2)\n", + "\n", + "# Add value labels on bars\n", + "for bar, time_val in zip(bars2, times):\n", + " height = bar.get_height()\n", + " ax2.text(bar.get_x() + bar.get_width()/2., height + max(times) * 0.02,\n", + " f'{time_val:.2f}s', ha='center', va='bottom', fontweight='bold', fontsize=10)\n", + "\n", + "# Add vertical dashed lines between bars\n", + "for i in range(len(algorithms) - 1):\n", + " ax2.axvline(x=i + 0.5, color='gray', linestyle='--', alpha=0.6, linewidth=1)\n", + "\n", + "# Styling\n", + "plt.tight_layout()\n", + "plt.subplots_adjust(bottom=0.2)\n", + "fig.patch.set_facecolor('#f8f9fa')\n", + "ax1.set_facecolor('#ffffff')\n", + "ax2.set_facecolor('#ffffff')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "pqulu6a1sj", + "metadata": {}, + "source": [ + "### Interpreting the Comparison Results\n", + "\n", + "**Key Observations:**\n", + "\n", + "1. **Solution Quality (Constraint Violations)**:\n", + " - **Green bars (value = 0)**: Algorithms that found perfect solutions satisfying all constraints\n", + " - **Red bars (value > 0)**: Algorithms with constraint violations\n", + " - For this instance, multiple algorithms can find optimal solutions, demonstrating that the problem is solvable at this scale\n", + "\n", + "2. **Execution Time Trade-offs**:\n", + " - **Brute Force**: Guaranteed optimal but time increases exponentially with problem size ($O(2^n)$)\n", + " - **Simulated Annealing**: Often finds good solutions with reasonable time, but no guarantees\n", + " - **Random Search**: Highly variable performance; may require many samples to find optimal solutions\n", + " - **Quantum (bf-DCQO)**: Execution time includes quantum circuit preparation, hardware queue time, and post-processing\n", + "\n", + "3. **Scalability Implications**:\n", + " - For 20 variables: All methods can potentially succeed\n", + " - For 30+ variables: Brute force becomes impractical ($2^{30} \\approx 1$ billion evaluations)\n", + " - For 50+ variables: Only quantum and sophisticated heuristics remain viable\n", + " - For 100+ variables: Quantum optimization with efficient algorithms like bf-DCQO offers the most promising path\n", + "\n", + "4. **When to Use Each Approach**:\n", + " - **Brute Force**: Only for small problems (< 25 variables) where guaranteed optimality is required\n", + " - **Simulated Annealing**: Medium-sized problems where good solutions are acceptable and classical resources are limited\n", + " - **Random Search**: Baseline for comparison or when solutions are abundant in the search space\n", + " - **Quantum (bf-DCQO)**: Large-scale problems (up to 156 qubits) where classical methods struggle, especially when high-quality solutions are needed and quantum hardware is accessible\n", + "\n", + "**The Quantum Advantage Horizon:**\n", + "\n", + "While classical methods may perform competitively at small scales like this 20-variable problem, quantum optimization shines as problems grow larger. The bf-DCQO algorithm's efficiency (10× fewer gates than other quantum approaches) positions it uniquely for the NISQ era, handling problem sizes that challenge both classical solvers and less efficient quantum algorithms." + ] + }, + { + "cell_type": "markdown", + "id": "conclusion", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "### What We Accomplished\n", + "\n", + "In this tutorial, we successfully:\n", + "\n", + "1. **Loaded a Real Optimization Problem**: Obtained a challenging Market Split instance from the QOBLIB benchmark library [2]\n", + "2. **Transformed to QUBO Format**: Converted the constrained problem into an unconstrained quadratic formulation [3]\n", + "3. **Leveraged Advanced Quantum Algorithms**: Used Kipu Quantum's bf-DCQO algorithm with counterdiabatic terms [1]\n", + "4. **Obtained Optimal Solutions**: Found feasible solutions satisfying all constraints\n", + "\n", + "### Key Takeaways\n", + "\n", + "**Algorithm Innovation**: The bf-DCQO algorithm represents a significant advancement [1]:\n", + "- **10 times fewer gates** than Digital Quantum Annealing\n", + "- **Approximately 10 iterations** instead of approximately 100 for variational methods\n", + "- **Built-in error resilience** through circuit efficiency\n", + "\n", + "**Counterdiabatic Terms**: Enable rapid quantum evolution while maintaining ground state fidelity, making quantum optimization practical on today's noisy hardware [1].\n", + "\n", + "**Bias-Field Guidance**: The iterative bias-field approach allows each iteration to start near previously found good solutions, providing a form of quantum-enhanced local search [1].\n", + "\n", + "**Practical Quantum Advantage**: For problems like Market Split that challenge classical solvers at small scales [4], bf-DCQO provides a viable path forward.\n", + "\n", + "### Next Steps\n", + "\n", + "To deepen your understanding and explore further:\n", + "\n", + "1. **Try Different Instances**: Experiment with other QOBLIB instances of varying sizes\n", + "2. **Tune Parameters**: Adjust `num_iterations`, `preprocessing_level`, `postprocessing_level`\n", + "3. **Compare with Classical**: Benchmark against classical optimization solvers\n", + "4. **Try different strategies**: Try to find a better encoding for the problem or formulate it as HUBO (if possible)\n", + "5. **Apply to Your Domain**: Adapt the QUBO/HUBO formulation techniques to your own optimization problems\n", + "\n", + "### References\n", + "\n", + "[1] IBM Quantum. \"Kipu Quantum Optimization.\" *IBM Quantum Documentation*. https://quantum.cloud.ibm.com/docs/en/guides/kipu-optimization\n", + "\n", + "[2] QOBLIB - Quantum Optimization Benchmarking Library. Zuse Institute Berlin (ZIB). 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: A Quarterly Journal of Operations Research*, 17(4), 335-371.\n", + "\n", + "[4] Lodi, A., Tramontani, A., & Weninger, K. (2023). \"The Intractable Decathlon: Benchmarking Hard Combinatorial Problems.\" *INFORMS Journal on Computing*." + ] + }, + { + "cell_type": "markdown", + "id": "896b1ab8", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "qiskit-function-update", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}