פתרון בעיית פיצול השוק עם Iskay Quantum Optimizer של Kipu Quantum
פונקציות Qiskit הן תכונה ניסיונית הזמינה רק למשתמשי IBM Quantum® Premium Plan, Flex Plan, ו-On-Prem (דרך IBM Quantum Platform API) Plan. הן נמצאות בסטטוס פרסום תצוגה מקדימה וכפופות לשינויים.
הערכת שימוש: 20 שניות על מעבד Heron r2. (הערה: זוהי הערכה בלבד. זמן הריצה בפועל עשוי להשתנות.)
רקע
מדריך זה מדגים כיצד לפתור את בעיית פיצול השוק באמצעות Iskay quantum optimizer של Kipu Quantum [1]. בעיית פיצול השוק מייצגת אתגר אמיתי של הקצאת משאבים, שבו יש לחלק שווקים לאזורי מכירה מאוזנים כדי לעמוד ביעדי ביקוש מדויקים.
אתגר פיצול השוק
בעיית פיצול השוק מציגה אתגר שנראה פשוט אך קשה חישובית בתחום הקצאת משאבים. נתונה חברה עם מוצרים הנמכרים ב- שווקים שונים, כאשר כל שוק רוכש חבילת מוצרים ספציפית (המיוצגת על ידי עמודות המטריצה ). יעד העסק הוא לחלק את השווקים הללו לשני אזורי מכירה מאוזנים, כך שכל אזור יקבל בדיוק מחצית מהביקוש הכולל לכל מוצר.
ניסוח מתמטי:
אנחנו מחפשים וקטור השמה בינארי , שבו:
- משייך את שוק לאזור A
- משייך את שוק לאזור B
- האילוץ חייב להתקיים, כאשר מייצג את יעדי המכירות (בדרך כלל מחצית מהביקוש הכולל לכל מוצר)
פונקציית העלות:
לפתרון הבעיה, אנחנו ממזערים את הפרת האילוץ בריבוע:
כאשר:
- מייצג את המכירות של מוצר בשוק
- הוא ההשמה הבינארית של שוק
- הוא יעד המכירות למוצר בכל אזור
- העלות שווה לאפס בדיוק כאשר כל האילוצים מתקיימים
כל איבר בסכום מייצג את הסטייה בריבוע מיעד המכירות למוצר מסוים. כאשר מרחיבים את פונקציית העלות, מקבלים:
מאחר ש- הוא קבוע, מיזור שקול למיזור הפונקציה הריבועית , שהיא בדיוק בעיית QUBO (אופטימיזציה בינארית ריבועית ללא אילוצים).
מורכבות חישובית:
למרות הפרשנות העסקית הפשוטה שלה, הבעיה מציגה קושי חישובי ניכר:
- כישלון בקנה מידה קטן: פותרי תכנות שלם מעורב (Mixed Integer Programming) קונבנציונליים נכשלים על מופעים עם כמ ה שבעה מוצרים בלבד תחת פסק זמן של שעה אחת [4]
- גידול אקספוננציאלי: מרחב הפתרונות גדל באופן אקספוננציאלי ( השמות אפשריות), מה שהופך גישות כוח גס לבלתי ישימות
מחסום חישובי חמור זה, בשילוב עם הרלוונטיות המעשית שלו לתכנון טריטוריה והקצאת משאבים, הופך את בעיית פיצול השוק לבנצ'מרק אידיאלי לאלגוריתמי אופטימיזציה קוונטיים [4].
מה הופך את גישת Iskay לייחודית?
מייעל Iskay משתמש באלגוריתם bf-DCQO (אופטימיזציה קוונטית קאונטר-דיאבטית דיגיטלית עם שדה הטיה) [1], המהווה התקדמות משמעותית באופטימיזציה קוונטית:
יעילות מעגל: אלגוריתם bf-DCQO משיג צמצום שערים משמעותי [1]:
- עד 10 פעמים פחות שערי סיבוך מאשר Digital Quantum Annealing (DQA)
- מעגלים רדודים משמעותית מאפשרים:
- פחות הצטברות שגיאות במהלך הביצוע הקוונטי
- יכולת להתמודד עם בעיות גדולות יותר על חומרה קוונטית נוכחית
- אין צורך בטכניקות הפחתת שגיאות
עיצוב לא-ווריאציוני: בניגוד לאלגוריתמים ווריאציוניים הדורשים כ-100 איטרציות, bf-DCQO בדרך כלל זקוק רק לכ-10 איטרציות [1]. זה מושג באמצעות:
- חישובי שדה הטיה חכמים מתוך התפלגויות מצב נמדדות
- התחלת כל איטרציה ממצב אנרגיה קרוב לפתרון הקודם
- עיבוד קלאסי לאחר-עיבוד משולב עם חיפוש מקומי
פרוטוקולים קאונטר-דיאבטיים: האלגוריתם משלב איברים קאונטר-דיאבטיים המדכאים עירורים קוונטיים לא רצויים בזמני התפתחות קצרים, ומאפשרים למערכת להישאר קרוב למצב הבסיסי אפילו עם מעברים מהירים [1].
דרישות מקדימות
לפני שמתחילים מדריך זה, יש לוודא שהרכיבים הבאים מותקנים:
- Qiskit IBM Runtime (
pip install qiskit-ibm-runtime) - Qiskit Functions (
pip install qiskit-ibm-catalog) - NumPy (
pip install numpy) - Requests (
pip install requests) - Opt Mapper Qiskit addon (
pip install qiskit-addon-opt-mapper)
כמו כן, יש להשיג גישה לפונקציית Iskay Quantum Optimizer מקטלוג פונקציות Qiskit.
הגדרה
ראשית, ייבא את כל החבילות הנדרשות למדריך זה.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit-addon-opt-mapper qiskit-ibm-catalog requests
import os
import tempfile
import time
from typing import Tuple, Optional
import numpy as np
import requests
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_addon_opt_mapper import OptimizationProblem
from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo
print("All required libraries imported successfully")
הגדרת פרטי גישה ל-IBM Quantum
הגדר את פרטי הגישה ל-IBM Quantum® Platform. תזדקק ל:
- API Token: מפתח ה-API בן 44 התווים שלך מ-IBM Quantum Platform
- Instance CRN: מזהה מופע IBM Cloud® שלך
token = "<YOUR_API_KEY>"
instance = "<YOUR_INSTANCE_CRN>"
שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית
אנחנו מתחילים במיפוי הבעיה הקלאסית שלנו לייצוג תואם-קוונטי. שלב זה כולל:
- התחברות ל-Iskay Quantum Optimizer
- טעינת בעיית פיצול השוק וניסוחה
- הבנת אלגוריתם bf-DCQO שיפתור אותה
התחברות ל-Iskay Quantum Optimizer
אנחנו מתחילים בביסוס חיבור לקטלוג פונקציות Qiskit וטעינת Iskay Quantum Optimizer. מייעל Iskay הוא פונקציה קוונטית שסופקה על ידי Kipu Quantum ומממשת את אלגוריתם bf-DCQO לפתרון בעיות אופטימיזציה על חומרה קוונטית.
catalog = QiskitFunctionsCatalog(token=token, instance=instance)
iskay_solver = catalog.load("kipu-quantum/iskay-quantum-optimizer")
print("Iskay optimizer loaded successfully")
print("Ready to solve optimization problems using bf-DCQO algorithm")
טעינת הבעיה וניסוחה
הבנת פורמט נתוני הבעיה
מופעי בעיות מ-QOBLIB (ספריית בנצ'מרקים לאופטימיזציה קוונטית) [2] מאוחסנים בפורמט טקסט פשוט. בואו נבחן את התוכן הממשי של מופע היעד שלנו ms_03_200_177.dat:
3 20
60 92 161 53 97 2 75 81 6 139 132 45 108 112 181 93 152 200 164 51 1002
176 196 41 143 2 88 0 79 10 71 75 148 82 135 34 187 33 155 58 46 879
68 68 179 173 127 163 48 49 99 78 44 52 173 131 73 198 84 109 180 95 1040
מבנה הפורמט:
-
שורה ראשונה:
3 203= מספר המוצרים (אילוצים/שורות במטריצה )20= מספר השווקים (משתנים/עמודות במטריצה )
-
3 השורות הבאות: מטריצת המקדמים וקטור היעד
- כל שורה מכילה 21 מספרים: 20 הראשונים הם מקדמי השורה, האחרון הוא היעד
- שורה 2:
60 92 161 ... 51 | 1002- 20 המספרים הראשונים: כמה ממוצר 1 כל אחד מ-20 השווקים מוכר
- המספר האחרון (1002): יעד המכירות למוצר 1 באזור אחד
- שורה 3:
176 196 41 ... 46 | 879- מכירות מוצר 2 לשוק ויעד (879)
- שורה 4:
68 68 179 ... 95 | 1040- מכירות מוצר 3 לשוק ויעד (1040)
פרשנות עסקית:
- שוק 0 מוכר: 60 יחידות של מוצר 1, 176 יחידות של מוצר 2, 68 יחידות של מוצר 3
- שוק 1 מוכר: 92 יחידות של מוצר 1, 196 יחידות של מוצר 2, 68 יחידות של מוצר 3
- וכך הלאה עבור כל 20 השווקים...
- מטרה: לפצל 20 שווקים אלו לשני אזורים שבהם כל אזור מקבל בדיוק 1002 יחידות של מוצר 1, 879 יחידות של מוצר 2 ו-1040 יחידות של מוצר 3
המרה ל-QUBO
מאילוצים ל-QUBO: הטרנספורמציה המתמטית
כוחה של האופטימיזציה הקוונטית טמון בהמרת בעיות עם אילוצים לצורות ריבועיות ללא אילוצים [4]. עבור בעיית פיצול השוק, אנחנו ממירים את אילוצי השוויון
כאשר , ל-QUBO על ידי הטלת קנס על הפרות אילוצים.
שיטת הקנס: מכיוון שעלינו ש- יתקיים במדויק, אנחנו ממזערים את ההפרה בריבוע:
זה שווה לאפס בדיוק כאשר כל האילוצים מתקיימים. הרחבה אלגברית:
יעד QUBO: מאחר ש- קבוע, האופטימיזציה שלנו הופכת ל:
תובנה מרכזית: טרנספורמציה זו היא מדויקת, לא משוערת. אילוצי שוויון מתרבעים באופן טבעי לצורה ריבועית ללא צורך במשתנים עזר או פרמטרי קנס — מה שהופך ניסוח זה לאלגנטי מתמטית ויעיל חישובית עבור פותרים קוונטיים [4]. נשתמש במחלקה OptimizationProblem להגדרת בעייתנו עם אילוצים, ולאחר מכן נמיר אותה לפורמט QUBO באמצעות OptimizationProblemToQubo, שניהם מחבילת qiskit_addon_opt_mapper. זה מטפל אוטומטית בטרנספורמציה מבוססת הקנס.
מימוש פונקציות טעינת נתונים והמרת QUBO
כעת נגדיר שתי פונקציות עזר:
parse_marketsplit_dat()- מנתחת את פורמט קובץ ה-.datומחלצת מטריצות ו-fetch_marketsplit_data()- מורידה מופעי בעיות ישירות מהמאגר של QOBLIB
def parse_marketsplit_dat(filename: str) -> Tuple[np.ndarray, np.ndarray]:
"""
Parse a market split problem from a .dat file format.
Parameters
----------
filename : str
Path to the .dat file containing the market split problem data.
Returns
-------
A : np.ndarray
Coefficient matrix of shape (m, n) where m is the number of products
and n is the number of markets.
b : np.ndarray
Target vector of shape (m,) containing the target sales per product.
"""
with open(filename, "r", encoding="utf-8") as f:
lines = [
line.strip()
for line in f
if line.strip() and not line.startswith("#")
]
if not lines:
raise ValueError("Empty or invalid .dat file")
# First line: m n (number of products and markets)
m, n = map(int, lines[0].split())
# Next m lines: each row of A followed by corresponding element of b
A, b = [], []
for i in range(1, m + 1):
values = list(map(int, lines[i].split()))
A.append(values[:-1]) # First n values: product sales per market
b.append(values[-1]) # Last value: target sales for this product
return np.array(A, dtype=np.int32), np.array(b, dtype=np.int32)
def fetch_marketsplit_data(
instance_name: str = "ms_03_200_177.dat",
) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]:
"""
Fetch market split data directly from the QOBLIB repository.
Parameters
----------
instance_name : str
Name of the .dat file to fetch (default: "ms_03_200_177.dat").
Returns
-------
A : np.ndarray or None
Coefficient matrix if successful, None if failed.
b : np.ndarray or None
Target vector if successful, None if failed.
"""
url = f"https://git.zib.de/qopt/qoblib-quantum-optimization-benchmarking-library/-/raw/main/01-marketsplit/instances/{instance_name}"
try:
response = requests.get(url, timeout=30)
response.raise_for_status()
with tempfile.NamedTemporaryFile(
mode="w", suffix=".dat", delete=False, encoding="utf-8"
) as f:
f.write(response.text)
temp_path = f.name
try:
return parse_marketsplit_dat(temp_path)
finally:
os.unlink(temp_path)
except Exception as e:
print(f"Error: {e}")
return None, None
טעינת מופע הבעיה
כעת נטעין את מופע הבעיה הספציפי ms_03_200_177.dat מ-QOBLIB [2]. למופע זה:
- 3 מוצרים (אילוצים)
- 20 שווקים (משתני החלטה בינאריים)
- למעלה ממיליון השמות שוק אפשריות לחקור ()
# Load the problem instance
instance_name = "ms_03_200_177.dat"
A, b = fetch_marketsplit_data(instance_name=instance_name)
if A is not None:
print("Successfully loaded problem instance from QOBLIB")
print("\nProblem Instance Analysis:")
print("=" * 50)
print(f"Coefficient Matrix A: {A.shape[0]} × {A.shape[1]}")
print(f" → {A.shape[0]} products (constraints)")
print(f" → {A.shape[1]} markets (decision variables)")
print(f"Target Vector b: {b}")
print(" → Target sales per product for each region")
print(
f"Solution Space: 2^{A.shape[1]} = {2**A.shape[1]:,} possible assignments"
)
המרה לפורמט QUBO
כעת נמיר את בעיית האופטימיזציה עם האילוצים לפורמט QUBO:
# Create optimization problem
ms = OptimizationProblem(instance_name.replace(".dat", ""))
# Add binary variables (one for each market)
ms.binary_var_list(A.shape[1])
# Add equality constraints (one for each product)
for idx, rhs in enumerate(b):
ms.linear_constraint(A[idx, :], sense="==", rhs=rhs)
# Convert to QUBO with penalty parameter
qubo = OptimizationProblemToQubo(penalty=1).convert(ms)
print("QUBO Conversion Complete:")
print("=" * 50)
print(f"Number of variables: {qubo.get_num_vars()}")
print(f"Constant term: {qubo.objective.constant}")
print(f"Linear terms: {len(qubo.objective.linear.to_dict())}")
print(f"Quadratic terms: {len(qubo.objective.quadratic.to_dict())}")
המרת QUBO לפורמט Iskay
כעת עלינו להמיר את אובייקט ה-QUBO לפורמט מילון הנדרש על ידי Iskay Optimizer של Kipu Quantum.
הארגומנטים problem ו-problem_type מקודדים בעיית אופטימיזציה בצורה
כאשר
- על ידי בחירת
problem_type = "binary", מציינים שפונקציית העלות היא בפורמטbinary, כלומר ש-, כלומר פונקציית העלות כתובה בניסוח QUBO/HUBO. - לעומת זאת, על ידי בחירת
problem_type = "spin", פונקציית העלות כתובה בניסוח Ising, שבו .
מקדמי הבעיה יש לקודד במילון באופן הבא:
שים לב שמפתחות המילון חייבים להיות מחרוזות המכילות טאפל חוקי של מספרים שלמים לא חוזרים. עבור בעיות בינאריות, ידוע שׁ:
עבור (מאחר ש- פירושו ). לכן, בניסוח ה-QUBO שלך, אם יש לך גם תרומות לינאריות וגם תרומות ריבועיות אלכסוניות , יש לשלב את האיברים הללו למקדם לינארי יחיד:
מקדם לינארי כולל למשתנה :
פירוש הדבר:
- איברים לינאריים כגון
"(i, )"מכילים: מקדם לינארי מקורי + מקדם ריבועי אלכסוני - אי ברים ריבועיים אלכסוניים כגון
"(i, i)"לא אמורים להופיע במילון הסופי - רק איברים ריבועיים לא-אלכסוניים כגון
"(i, j)"שבהם יש לכלול כערכים נפרדים
דוגמה: אם ה-QUBO שלך מכיל , מילון Iskay אמור להכיל:
"(0, )":5.0(שילוב )"(0, 1)":4.0(איבר לא-אלכסוני)
ולא ערכים נפרדים עבור "(0, )": 3.0 ו-"(0, 0)": 2.0.
# Convert QUBO to Iskay dictionary format:
# Create empty Iskay input dictionary
iskay_input_problem = {}
# Convert QUBO to Iskay dictionary format
iskay_input_problem = {"()": qubo.objective.constant}
for i in range(qubo.get_num_vars()):
for j in range(i, qubo.get_num_vars()):
if i == j:
# Add linear term (including diagonal quadratic contribution)
iskay_input_problem[f"({i}, )"] = float(
qubo.objective.linear.to_dict().get(i)
) + float(qubo.objective.quadratic.to_dict().get((i, i)))
else:
# Add off-diagonal quadratic term
iskay_input_problem[f"({i}, {j})"] = float(
qubo.objective.quadratic.to_dict().get((i, j))
)
# Display Iskay dictionary summary
print("Iskay Dictionary Format:")
print("=" * 50)
print(f"Total coefficients: {len(iskay_input_problem)}")
print(f" • Constant term: {iskay_input_problem['()']}")
print(
f" • Linear terms: {sum(1 for k in iskay_input_problem.keys() if k != '()' and ', )' in k)}"
)
print(
f" • Quadratic terms: {sum(1 for k in iskay_input_problem.keys() if k != '()' and ', )' not in k)}"
)
print("\nSample coefficients:")
# Get first 10 and last 5 items properly
items = list(iskay_input_problem.items())
first_10 = list(enumerate(items[:10]))
last_5 = list(enumerate(items[-5:], start=len(items) - 5))
for i, (key, value) in first_10 + last_5:
coeff_type = (
"constant"
if key == "()"
else "linear"
if ", )" in key
else "quadratic"
)
print(f" {key}: {value} ({coeff_type})")
print(" ...")
print("\n✓ Problem ready for Iskay optimizer!")
הבנת אלגוריתם bf-DCQO
לפני שנריץ את האופטימיזציה, בואו נבין את האלגוריתם הקוונטי המתוחכם המניע את Iskay: bf-DCQO (אופטימיזציה קוונטית קאונטר-דיאבטית דיגיטלית עם שדה הטיה) [1].
מהו bf-DCQO?
bf-DCQO מבוסס על התפתחות זמן של מערכת קוונטית שבה פתרון הבעיה מקודד במצב הבסיסי (מצב האנרגיה הנמוכה ביותר) של ההמילטוניאן הקוונטי הסופי [1]. האלגוריתם מתמודד עם אתגר בסיסי באופטימיזציה קוונטית:
האתגר: מחשוב קוונטי אדיאבטי מסורתי דורש התפתחות איטית מאוד לשמירה על תנאי מצב הבסיסי בהתאם למשפט האדיאבטי. זה דורש מעגלים קוונטיים עמוקים יותר ויותר ככל שמורכבות הבעיה גדלה, מה שמוביל ליותר פעולות שערים ושגיאות מצטברות.