Lumaktaw sa pangunahing nilalaman
Ang proyektong ito ay nasa beta. Nakakita ng bug? Mag-bukas ng GitHub issue. May tanong o ideya? Magtanong sa Discussions — gusto naming marinig ang iyong feedback!
Gaano kaganda ang kalidad ng pagsasalin sa pahinang ito?

Lutasin ang Sherrington-Kirkpatrick model gamit ang ParityQC Parity Twine Optimizer

Tinatayang paggamit: 10 segundo sa isang Nighthawk r2 processor. (TANDAAN: Isa lamang itong pagtatantya. Maaaring mag-iba ang iyong runtime.)

Mga inaasahang matutunan​

  • Gamitin ang Parity Twine Optimizer upang lutasin ang Sherrington-Kirkpatrick model.

  • Alamin kung aling mga opsyon ng Parity Twine Optimizer ang available at kung anong mga resulta ang inilalabas nito.

Kaligiran​

Ipinapakita ng tutorial na ito kung paano lutasin ang Sherrington-Kirkpatrick model gamit ang ParityQC Parity Twine Optimizer.

Nagbibigay ito ng code para buuin ang problema nang lokal lamang, sa format na tinatanggap ng Parity Twine Optimizer.

Ang Sherrington-Kirkpatrick model​

Ang Sherrington-Kirkpatrick (SK) model ay isang pundasyong modelo sa statistical mechanics, partikular sa pag-aaral ng spin glasses. Hindi tulad ng karaniwang Ising model, kung saan ang mga interaksyon ay karaniwang limitado lamang sa pinakamalapit na kapitbahay, ang SK model ay isang infinite-range na modelo, ibig sabihin bawat spin ay nakikipag-ugnayan sa bawat ibang spin sa sistema. Nagreresulta ito sa isang lubhang kumplikado, "magaspang" ("rugged"), na energy landscape na nailalarawan ng maraming local minima, na siyang katangian ng glassy na pag-uugali.

Ang pangunahing katangian ng SK model ay nasa frustration. Sa modelo, ang mga interaction strengths na JijJ_{ij} sa pagitan ng mga spin ay random na ipinamamahagi sa pagitan ng positibo at negatibong mga halaga. Nagreresulta ito sa mga sitwasyon (halimbawa, sa triangular na mga ayos) kung saan hindi maaaring ayusin ang mga spin upang mabawasan ang lahat ng interaksyon nang sabay-sabay. Sa SK model, dahil bawat spin ay nakikipag-ugnayan sa bawat ibang spin, ang frustration na ito ay lalong lumalala sa buong sistema, na humahantong sa isang network ng nagkakasalungat na mga hadlang.

Pormulasyong matematikal​

Ang estado ng sistema ay tinutukoy ng isang set ng NN na Ising spins, si∈{+1,−1}s_i \in \{+1, -1 \} . Ang enerhiya ng isang partikular na configuration ay ibinibigay ng Hamiltonian:

H=−∑1≤i≤j≤NJijsisjH = - \sum_{1 \leq i \leq j \leq N} J_{ij} s_i s_j

kung saan ang JijJ_{ij} ay ang coupling strength sa pagitan ng spin ii at spin jj.

Sa SK model, ang mga coupling na JijJ_{ij} ay independent at identically distributed random variables. Upang matiyak na ang enerhiya ay mananatiling extensive (proporsyonal sa NN) habang N→∞N \rightarrow \infty, dapat mag-scale ang variance ng mga coupling ayon sa bilang ng mga particle:

Jij∼N(0,J2N)J_{ij} \sim \mathcal{N} \left( 0, \frac{J^2}{N} \right)

Ang ground state para sa isang ibinigay na HH ay ang partikular na configuration ng mga spin (s1,s2,...sns_1, s_2, ...s_n), na nagpapaliit sa enerhiya. Ang paghahanap ng ground state ay isang NP-hard na optimization problem.

Mga Kinakailangan​

Bago simulan ang tutorial na ito, tiyaking naka-install ang mga sumusunod:

  • Qiskit Functions Catalog IBM Client (pip install qiskit-ibm-catalog)

  • Qiskit addon Optimization Mapper (pip install qiskit_addon_opt_mapper)

  • NumPy (pip install numpy)

Kailangan mo rin ng permiso upang ma-access ang ParityQC Twine Optimizer function. Upang humiling ng access, kumpletuhin ang form na ito.

Pag-setup​

(Ipinapalagay ng code na ito na na-save mo na ang iyong account sa iyong local environment.)

Una, i-import ang lahat ng kinakailangang packages para sa tutorial na ito.

# 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

I-load ang Parity Twine Optimizer mula sa Qiskit Functions catalog:

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
function = catalog.load("parityqc/parity-twine-optimizer")

Hakbang 1: Tukuyin ang problema bilang isang objective function​

Sa halip na kunin ang SK problem mula sa isang library gaya ng ginagawa natin para sa Market Split problem, direkta mo itong bubuuin.

Bubuuin ng function na generate_sk_problem ang SK problem nang direkta sa kinakailangang dictionary format. Ang tanging input na kinakailangan ay ang n, ang bilang ng mga spin sa modelo.

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

Gamitin ang function na _generate_couplings_sk_model upang kalkulahin ang random na coupling terms sa SK model. Para sa higit na kontrol sa mga coupling, maaari kang gumamit ng mga opsyonal na argumento, na ipinaliliwanag sa docstring ng function.

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

Hakbang 2: Lutasin ang problema gamit ang Parity Twine Optimizer​

Gamit ang mga function sa itaas, maaari mong i-set up ang SK problem at maghanap ng solusyon gamit ang Twine Optimizer at isang piniling IBM Quantum® backend.

Upang patakbuhin ang function, pumili ng angkop na backend; halimbawa, ibm_phoenix.

Opsyonal, gumamit ng options para sa karagdagang kontrol sa submission:

options = {
"shots": 100000,
"postprocessing_level": 1,
"transpile_only": False,
"job_tags": ["sk"],
}

kung saan ang shots ay isang integer na tumutukoy sa bilang ng circuit executions, ang postprocessing_level ay nagpapasiya kung ilalapat ang post-processing sa resulta, ang transpile_only ay pumipili kung ang problema ay tine-transpile lamang sa isang circuit (at hindi nalulutas), at ang job_tags ay label na ginagamit upang tukuyin ang job sa IBM Quantum Platform.

Ang laki ng SK model ay tinutukoy ng NN, ang bilang ng mga nakikipag-ugnayang spin. Kapag pinili mo na ang NN, ang code sa itaas ay bubuo ng problema para sa n_spins.

Patakbuhin ang optimizer:

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}")

Suriin ang status ng job:

# Monitor the job status
function_job.status()

Kunin ang mga resulta:

result = function_job.result()

result

Ang resulta ay nasa form na:

{
'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},
},
}
}

kung saan ang solution dictionary ay tumutugma sa mga qubit na tinukoy sa problema at nagbibigay ng kanilang na-optimize na spin values para sa Hamiltonian ng SK model. Ang partikular na sunod-sunod na spin na ito sa optimal na solusyon ay kumakatawan sa configuration na nagpapaliit sa kabuuang enerhiya ng sistema batay sa ibinigay na random na interaction strengths. Sa SK model, maituturing itong pinakamababang energy state ng isang disordered magnetic system.

Ang metadata ay nagbibigay ng impormasyon tungkol sa transpilation (two qubit gates counts/depth, mga gate na ginamit, aktibong qubits) at iba't ibang runtimes.

Mga susunod na hakbang​

Mga Rekomendasyon