Source code for mindquantum.algorithm.qaia.SB

# Copyright 2023 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Simulated bifurcation (SB) algorithms and its variants."""
# pylint: disable=invalid-name
import numpy as np
from scipy.sparse import csr_matrix

from .QAIA import QAIA


class SB(QAIA):
    r"""
    The base class of SB.

    This class is the base class for SB. It contains the initialization of
    spin values and momentum.

    Args:
        J (Union[numpy.array, csr_matrix]): The coupling matrix with shape :math:`(N x N)`.
        h (numpy.array): The external field with shape :math:`(N, )`.
        x (numpy.array): The initialized spin value with shape :math:`(N x batch_size)`. Default: ``None``.
        n_iter (int): The number of iterations. Default: ``1000``.
        batch_size (int): The number of sampling. Default: ``1``.
        dt (float): The step size. Default: ``1``.
        xi (float): positive constant with the dimension of frequency. Default: ``None``.
    """

    # pylint: disable=too-many-arguments
    def __init__(
        self,
        J,
        h=None,
        x=None,
        n_iter=1000,
        batch_size=1,
        dt=1,
        xi=None,
    ):
        """Construct SB algorithm."""
        super().__init__(J, h, x, n_iter, batch_size)
        self.J = csr_matrix(self.J)
        # positive detuning frequency
        self.delta = 1
        self.dt = dt
        # pumping amplitude
        self.p = np.linspace(0, 1, self.n_iter)
        self.xi = xi
        if self.xi is None:
            self.xi = 0.5 * np.sqrt(self.N - 1) / np.sqrt(csr_matrix.power(self.J, 2).sum())
        self.x = x

        self.initialize()

    def initialize(self):
        """Initialize spin values and momentum."""
        if self.x is None:
            self.x = 0.02 * (np.random.rand(self.N, self.batch_size) - 0.5)

        if self.x.shape[0] != self.N:
            raise ValueError(f"The size of x {self.x.shape[0]} is not equal to the number of spins {self.N}")

        self.y = 0.02 * (np.random.rand(self.N, self.batch_size) - 0.5)


[docs]class ASB(SB): # noqa: N801 r""" Adiabatic SB algorithm. Reference: `Combinatorial optimization by simulating adiabatic bifurcations in nonlinear Hamiltonian systems <https://www.science.org/doi/10.1126/sciadv.aav2372>`_. Args: J (Union[numpy.array, csr_matrix]): The coupling matrix with shape :math:`(N x N)`. h (numpy.array): The external field with shape :math:`(N, )`. x (numpy.array): The initialized spin value with shape :math:`(N x batch_size)`. Default: ``None``. n_iter (int): The number of iterations. Default: ``1000``. batch_size (int): The number of sampling. Default: ``1``. dt (float): The step size. Default: ``1``. xi (float): positive constant with the dimension of frequency. Default: ``None``. M (int): The number of update without mean-field terms. Default: ``2``. """ # pylint: disable=too-many-arguments def __init__( self, J, h=None, x=None, n_iter=1000, batch_size=1, dt=1, xi=None, M=2, ): """Construct ASB algorithm.""" super().__init__(J, h, x, n_iter, batch_size, dt, xi) # positive Kerr coefficient self.K = 1 self.M = M # Time step for updating without mean-field terms self.dm = self.dt / self.M
[docs] def update(self): """Dynamical evolution based on Modified explicit symplectic Euler method.""" # iterate on the number of MVMs for i in range(self.n_iter): for _ in range(self.M): self.x += self.dm * self.y * self.delta self.y -= (self.K * self.x**3 + (self.delta - self.p[i]) * self.x) * self.dm if self.h is None: self.y += self.xi * self.dt * self.J.dot(self.x) else: self.y += self.xi * self.dt * (self.J.dot(self.x) + self.h)
[docs]class BSB(SB): # noqa: N801 r""" Ballistic SB algorithm. Reference: `High-performance combinatorial optimization based on classical mechanics <https://www.science.org/doi/10.1126/sciadv.abe7953>`_. Args: J (Union[numpy.array, csr_matrix]): The coupling matrix with shape :math:`(N x N)`. h (numpy.array): The external field with shape :math:`(N, )`. x (numpy.array): The initialized spin value with shape :math:`(N x batch_size)`. Default: ``None``. n_iter (int): The number of iterations. Default: ``1000``. batch_size (int): The number of sampling. Default: ``1``. dt (float): The step size. Default: ``1``. xi (float): positive constant with the dimension of frequency. Default: ``None``. """ # pylint: disable=too-many-arguments def __init__( self, J, h=None, x=None, n_iter=1000, batch_size=1, dt=1, xi=None, ): """Construct BSB algorithm.""" super().__init__(J, h, x, n_iter, batch_size, dt, xi) self.initialize() # pylint: disable=attribute-defined-outside-init
[docs] def update(self): """Dynamical evolution based on Modified explicit symplectic Euler method.""" for i in range(self.n_iter): if self.h is None: self.y += (-(self.delta - self.p[i]) * self.x + self.xi * self.J.dot(self.x)) * self.dt else: self.y += (-(self.delta - self.p[i]) * self.x + self.xi * (self.J.dot(self.x) + self.h)) * self.dt self.x += self.dt * self.y * self.delta cond = np.abs(self.x) > 1 self.x = np.where(cond, np.sign(self.x), self.x) self.y = np.where(cond, np.zeros_like(self.x), self.y)
[docs]class DSB(SB): # noqa: N801 r""" Discrete SB algorithm. Reference: `High-performance combinatorial optimization based on classical mechanics <https://www.science.org/doi/10.1126/sciadv.abe7953>`_. Args: J (Union[numpy.array, csr_matrix]): The coupling matrix with shape :math:`(N x N)`. h (numpy.array): The external field with shape :math:`(N, )`. x (numpy.array): The initialized spin value with shape :math:`(N x batch_size)`. Default: ``None``. n_iter (int): The number of iterations. Default: ``1000``. batch_size (int): The number of sampling. Default: ``1``. dt (float): The step size. Default: ``1``. xi (float): positive constant with the dimension of frequency. Default: ``None``. """ # pylint: disable=too-many-arguments def __init__( self, J, h=None, x=None, n_iter=1000, batch_size=1, dt=1, xi=None, ): """Construct DSB algorithm.""" super().__init__(J, h, x, n_iter, batch_size, dt, xi) self.initialize() # pylint: disable=attribute-defined-outside-init
[docs] def update(self): """Dynamical evolution based on Modified explicit symplectic Euler method.""" for i in range(self.n_iter): if self.h is None: self.y += (-(self.delta - self.p[i]) * self.x + self.xi * self.J.dot(np.sign(self.x))) * self.dt else: self.y += ( -(self.delta - self.p[i]) * self.x + self.xi * (self.J.dot(np.sign(self.x)) + self.h) ) * self.dt self.x += self.dt * self.y * self.delta cond = np.abs(self.x) > 1 self.x = np.where(cond, np.sign(self.x), self.x) self.y = np.where(cond, np.zeros_like(self.y), self.y)