Розв'язання моделі Шеррінгтона-Кіркпатріка за допомогою ParityQC Parity Twine Optimizer
Оцінка часу виконання: 10 секунд на процесорі Nighthawk r2. (ПРИМІТКА: Це лише оцінка. Твій час виконання може відрізнятися.)
Результати навчання
-
Використай Parity Twine Optimizer для розв'язання моделі Шеррінгтона-Кіркпатріка.
-
Дізнайся, які опції доступні для Parity Twine Optimizer і які результати виводяться.
Загальні відомості
У цьому туторіалі показано, як розв'язати модель Шеррінгтона-Кіркпатріка за допомогою ParityQC Parity Twine Optimizer.
Він містить код для повністю локального формулювання задачі у форматі, сумісному з Parity Twine Optimizer.
Модель Шеррінгтона-Кіркпатріка
Модель Шеррінгтона-Кіркпатріка (SK) є фундаментальною моделлю статистичної механіки, зокрема в дослідженні спінових скл. На відміну від стандартної моделі Ізінга, де взаємодії зазвичай обмежені найближчими сусідами, модель SK є моделлю з нескінченним радіусом дії, тобто кожен спін взаємодіє з кожним іншим спіном системи. Це призводить до вкрай складного, "нерівного", енергетичного ландшафту з багатьма локальними мінімумами, що й є характерною ознакою склоподібної поведінки.
Ключовою особливістю моделі SK є фрустрація. У моделі сили взаємодії між спінами розподілені випадковим чином між додатними та від'ємними значеннями. Це призводить до ситуацій (наприклад, у трикутних конфігураціях), коли спіни не можуть бути розташовані так, щоб мінімізувати всі взаємодії одночасно. У моделі SK, оскільки кожен спін взаємодіє з кожним іншим спіном, ця фрустрація посилюється глобально, що призводить до мережі конфліктуючих обмежень.
Математичне формулювання
Стан системи визначається набором спінів Ізінга, . Енергія конкретної конфігурації задається гамільтоніаном:
де — це сила зв'язку між спіном та спіном .
У моделі SK зв'язки є незалежними та однаково розподіленими випадковими величинами. Щоб енергія залишалася екстенсивною (пропорційною ) при , дисперсія зв'язків повинна масштабуватися з кількістю частинок:
Основний стан для заданого — це конкретна конфігурація спінів (), яка мінімізує енергію. Пошук основного стану є NP-складною задачею оптимізації.
Вимоги
Перш ніж почати цей туторіал, переконайся, що встановлено наступне:
-
Qiskit Functions Catalog IBM Client (
pip install qiskit-ibm-catalog) -
Qiskit addon Optimization Mapper (
pip install qiskit_addon_opt_mapper) -
NumPy (
pip install numpy)
Тобі також потрібен дозвіл на доступ до функції ParityQC Twine Optimizer. Щоб подати запит на доступ, заповни цю форму.
Налаштування
(Цей код передбачає, що ти вже зберіг обліковий запис у своєму локальному середовищі.)
Спочатку імпортуй усі необхідні пакети для цього туторіалу.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit_ibm_catalog": "qiskit-ibm-catalog"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
from qiskit_ibm_catalog import QiskitFunctionsCatalog
Завантаж Parity Twine Optimizer з каталогу Qiskit Functions:
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
function = catalog.load("parityqc/parity-twine-optimizer")
Крок 1: Визначення задачі як цільової функції
Замість отримання задачі SK з бібліотеки, як ми робимо для задачі Market Split, ти формулюєш її безпосередньо.
Функція generate_sk_problem формулює задачу SK безпосередньо у потрібному форматі словника. Єдиним необхідним вхідним параметром є n — кількість спінів у моделі.
def generate_sk_problem(
n: int,
coupling_mean: float = 0.0,
coupling_std: float = 1.0,
local_fields_mean: float = 0.0,
local_fields_std: float = 0.0,
edge_density: float = 1.0,
ensure_extensivity: bool = False,
seed: int | None = None,
) -> dict:
"""Generate the Sherrington-Kirkpatrick (SK) model with varying
edge density.
Samples couplings and local fields via ``generate_couplings_sk_model``
and assembles the corresponding Ising Hamiltonian
H = -∑_{i<j} J_ij z_i z_j - ∑_i h_i z_i,
where z_i ∈ {-1, +1}.
Args:
n: Number of spins (>= 2).
coupling_mean: Mean coupling before optional SK scaling.
coupling_std: Coupling std before optional SK scaling.
local_fields_mean: Mean longitudinal field.
local_fields_std: Std of the longitudinal fields.
edge_density: Fraction of non-zero couplings, in ``[2/n, 1]``.
ensure_extensivity: Whether to apply the SK 1/n scaling.
seed: random number generator seed.
Returns:
An encoding the SK Hamiltonian.
Raises:
ValueError: If ``n < 2``, ``coupling_std < 0``, ``local_fields_std < 0``,
or ``edge_density`` is outside ``[2/n, 1]``.
"""
couplings, local_fields = _generate_couplings_sk_model(
n=n,
coupling_mean=coupling_mean,
coupling_std=coupling_std,
local_fields_mean=local_fields_mean,
local_fields_std=local_fields_std,
edge_density=edge_density,
ensure_extensivity=ensure_extensivity,
seed=seed,
)
# Handle quadratic terms: coupling[i, j] * zj[i] * zj[j]
# Only iterate over the upper triangle (i < j)
sk_problem = {
str((i, j)): float(couplings[i, j])
for i in range(n)
for j in range(i + 1, n)
if couplings[i, j] != 0
}
# Handle linear terms: local_fields[i] * zj[i]
sk_problem.update(
{
str((i,)): float(local_fields[i])
for i in range(n)
if local_fields[i] != 0
}
)
return sk_problem
Використай функцію _generate_couplings_sk_model для обчислення випадкових членів зв'язку в моделі SK. Для більшого контролю над зв'язками ти можеш використати додаткові аргументи, які пояснено в docstring функції.
def _generate_couplings_sk_model(
n: int,
coupling_mean: float = 0.0,
coupling_std: float = 1.0,
local_fields_mean: float = 0.0,
local_fields_std: float = 0.0,
edge_density: float = 1.0,
ensure_extensivity: bool = False,
seed: int | None = None,
) -> tuple[np.ndarray, np.ndarray]:
"""Generate random couplings and local fields for an Ising / SK model.
Couplings are Gaussian. With ``ensure_extensivity=True`` they follow the
Sherrington-Kirkpatrick scaling ``J_ij ~ N(coupling_mean/n, coupling_std^2/n)``
(extensive energy, O(n)); otherwise ``J_ij ~ N(coupling_mean, coupling_std^2)``
(energy O(n^2)). Fields are ``h_i ~ N(local_fields_mean, local_fields_std^2)``.
``edge_density`` sets the fraction of the ``n*(n-1)/2`` possible couplings that
are non-zero (1 = fully dense). The kept edges always include a random spanning
tree, so the interaction graph is guaranteed connected. This requires at least
``n-1`` edges, so ``edge_density`` must be at least ``2/n``.
Args:
n: Number of spins (>= 2).
coupling_mean: Mean coupling before optional SK scaling.
coupling_std: Coupling std before optional SK scaling.
local_fields_mean: Mean longitudinal field.
local_fields_std: Std of the longitudinal fields.
edge_density: Fraction of non-zero couplings, in ``[2/n, 1]``.
ensure_extensivity: Whether to apply the SK 1/n scaling.
seed: random number generator seed.
Returns:
Tuple ``(couplings, fields)``: a symmetric ``(n, n)`` matrix with zero
diagonal, and an ``(n,)`` field vector.
Raises:
ValueError: If ``n < 2``, ``coupling_std < 0``, ``local_fields_std < 0``,
or ``edge_density`` is outside ``[2/n, 1]``.
"""
if n < 2:
raise ValueError(f"n must be >= 2, got {n}")
if coupling_std < 0 or local_fields_std < 0:
raise ValueError(
"coupling_std and local_fields_std must be non-negative"
)
# A connected graph on n nodes needs at least n-1 of the n*(n-1)/2 possible
# edges, so edge_density has a hard lower bound of 2/n.
min_edge_density = 2.0 / n
if not min_edge_density <= edge_density <= 1.0:
raise ValueError(
f"edge_density must be in [{min_edge_density:.4g}, 1] for n={n} "
f"(at least n-1 edges are needed to keep the graph connected), "
f"got {edge_density}"
)
rng = np.random.default_rng(seed)
j_loc, j_scale = (
(coupling_mean / n, coupling_std / np.sqrt(n))
if ensure_extensivity
else (coupling_mean, coupling_std)
)
upper_idx = np.triu_indices(n, k=1)
n_edges = len(upper_idx[0])
# Select which edges are present.
if edge_density < 1.0:
n_keep = int(round(edge_density * n_edges))
# Map each (i, j) node pair to its position in the flat upper-triangle list.
pair_to_flat = {
(int(i), int(j)): idx
for idx, (i, j) in enumerate(
zip(upper_idx[0], upper_idx[1], strict=False)
)
}
# Random spanning tree: node perm[k] links to a random earlier node.
perm = rng.permutation(n)
keep = np.zeros(n_edges, dtype=bool)
for k in range(1, n):
child, parent = perm[k], perm[rng.integers(0, k)]
i, j = min(child, parent), max(child, parent)
keep[pair_to_flat[(int(i), int(j))]] = True
# Fill the remaining budget with random non-tree edges.
remaining = n_keep - (n - 1)
if remaining > 0:
keep[
rng.choice(
np.flatnonzero(~keep), size=remaining, replace=False
)
] = True
else:
keep = np.ones(n_edges, dtype=bool)
n_present = int(keep.sum())
if j_scale == 0.0:
vals = np.full(n_present, j_loc)
else:
vals = rng.normal(loc=j_loc, scale=j_scale, size=n_present)
couplings = np.zeros((n, n))
couplings[upper_idx[0][keep], upper_idx[1][keep]] = vals
couplings += couplings.T # symmetrize; diagonal stays zero
fields = (
np.full(n, local_fields_mean)
if local_fields_std == 0.0
else rng.normal(loc=local_fields_mean, scale=local_fields_std, size=n)
)
return couplings, fields
Крок 2: Розв'язання задачі за допомогою Parity Twine Optimizer
За допомогою наведених вище функцій ти можеш налаштувати задачу SK та знайти розв'язок за допомогою Twine Optimizer та обраного бекенда IBM Quantum®.
Щоб запустити функцію, обери відповідний бекенд, наприклад, ibm_phoenix.
За бажанням, використай options для додаткового контролю над надсиланням завдання:
options = {
"shots": 100000,
"postprocessing_level": 1,
"transpile_only": False,
"job_tags": ["sk"],
}
де shots — це ціле число, яке визначає кількість виконань схеми, postprocessing_level визначає, чи застосовується постобробка до результату,
transpile_only визначає, чи задачу лише транспілюють у схему (а не розв'язують), а job_tags — це мітка для ідентифікації завдання на IBM Quantum Platform.
Розмір моделі SK визначається — кількістю взаємодіючих спінів. Після того як ти обереш , наведений вище код генерує задачу для n_spins.
Запусти оптимізатор:
n_spins = 50
sk_problem = generate_sk_problem(n_spins)
function_job = function.run(
problem=sk_problem,
variable_type="spin",
backend_name="ibm_phoenix",
options=options,
)
print(f"Job ID: {function_job.job_id}")
Перевір статус завдання:
# Monitor the job status
function_job.status()
Отримай результати:
result = function_job.result()
result
Результат має таку форму:
{
'solution': {'0': 1, '1': 1, '10': 1, '11': 1, ... },
'objective_value': -240.5425312543882,
'solution_bitstring': '00001101110100100111001110101001101111111011001110',
'metadata': {
'circuit_metrics': {
'depth': 523,
'gate_count': 10118,
'two_qubit_gate_depth': 196,
'two_qubit_gate_count': 2499,
'num_qubits': 50,
'operations': {'sx': 3353, 'rz': 3320, 'cz': 2499, 'delay': 894, 'measure': 50, 'x': 2},
},
'solver_info': {
'variable_mapping': {'0': 0, '1': 1, '10': 2, '11': 3, ... },
'bitstring_distributions': {
'before_postprocessing': {'011101110010110111001110011000': 1, ... },
'after_postprocessing': {'011011110000110101001111011000': 1, ... }
},
'best_parameters': {
'beta': [-0.4602084830507902],
'gamma': [1.8500357096574955]
}
},
'resource_usage': {
'RUNNING: MAPPING': {'CPU_TIME': 290.272},
'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 0.494},
'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 8.775},
'RUNNING: EXECUTING_QPU': {'QPU_TIME': 31.0},
'RUNNING: POST_PROCESSING': {'CPU_TIME': 162.96},
},
}
}
де словник solution відповідає кубітам, визначеним у задачі, і надає їхні оптимізовані значення спінів для гамільтоніана моделі SK.
Ця конкретна послідовність спінів в оптимальному розв'язку представляє конфігурацію, яка мінімізує загальну енергію системи на основі заданих випадкових сил взаємодії.
У моделі SK це можна розглядати як стан з найнижчою енергією невпорядкованої магнітної системи.
metadata надає інформацію про транспіляцію (кількість/глибина двокубітних вентилів, використані вентилі, активні кубіти) та різні часи виконання.
Наступні кроки
- Подай запит на доступ до функції, заповнивши цю форму.
- Відвідай довідник API для цієї функції Qiskit.
- Прочитай посібник.
- Спробуй туторіал із застосуванням Parity Twine Optimizer до задачі Market Split.
- Переглянь препринт ArXiv Connectivity-aware Synthesis of Quantum Algorithms, Drier et al. (2025).