Source code for qiskit_optimization.applications.sk_model
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2021, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""An application class for the Sherrington Kirkpatrick (SK) model."""
from __future__ import annotations
import networkx as nx
import numpy as np
from docplex.mp.model import Model
from .graph_optimization_application import OptimizationApplication
from ..algorithms import OptimizationResult
from ..problems.quadratic_program import QuadraticProgram
from ..translators import from_docplex_mp
[docs]
class SKModel(OptimizationApplication):
    r"""Optimization application of the "Sherrington Kirkpatrick (SK) model" [1].
    The SK Hamiltonian over n spins is given as:
    :math:`H(x)=-1/\sqrt{n} \sum_{i<j} w_{i,j}x_ix_j`,
    where :math:`x_i\in\{\pm 1\}` is the configuration of spins and
    :math:`w_{i,j}\in\{\pm 1\}` is a disorder chosen independently and uniformly at random.
    Notice that there are other variants of disorders e.g., with :math:`w_{i,j}` chosen from
    the normal distribution with mean 0 and variance 1.
    References:
        [1]: Dmitry Panchenko. "The Sherrington-Kirkpatrick model: an overview",
        https://arxiv.org/abs/1211.1094
    """
    def __init__(self, num_sites: int, rng_or_seed: np.random.Generator | int | None = None):
        """
        Args:
            num_sites: number of sites
            rng_or_seed: NumPy pseudo-random number generator or seed for np.random.default_rng(<seed>)
                or None. None results in usage of np.random.default_rng().
        """
        if isinstance(rng_or_seed, np.random.Generator):
            self._rng = rng_or_seed
        else:
            self._rng = np.random.default_rng(rng_or_seed)
        self._num_sites = num_sites
        self._graph = nx.complete_graph(self._num_sites)
        self.disorder()
[docs]
    def disorder(self) -> None:
        """Generate a new disorder of the SK model."""
        for _, _, edge_data in self._graph.edges(data=True):
            edge_data["weight"] = self._rng.choice([-1, 1])
[docs]
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert an SK model problem instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`.
        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the SK problem instance.
        """
        mdl = Model(name="SK-model")
        x = mdl.binary_var_list(self._graph.number_of_nodes())
        objective = mdl.sum(
            -1
            / np.sqrt(self._num_sites)
            * self._graph.edges[i, j]["weight"]
            * (2 * x[i] - 1)
            * (2 * x[j] - 1)
            for i, j in self._graph.edges
        )
        # we converted the standard H(x)=-1/\sqrt{n} \sum w_{ij}x_ix_j, where x_i\in\pm 1 to binary.
        mdl.minimize(objective)
        return from_docplex_mp(mdl)
[docs]
    def interpret(self, result: OptimizationResult | np.ndarray) -> list[int]:
        """Interpret a result as configuration of spins.
        Args:
            result : The calculated result of the problem.
        Returns:
            configuration of spins
        """
        configuration = [2 * x - 1 for x in self._result_to_x(result)]
        return configuration
    @property
    def graph(self) -> nx.Graph:
        """Getter of the graph representation.
        Returns:
            A graph for a problem.
        """
        return self._graph
    @property
    def num_sites(self) -> int:
        """Getter of the number of sites.
        Returns:
            Number of sites.
        """
        return self._num_sites