
    ^Mhr                         d dl Zd dlZd dlZd dlZddlmZmZ ddlm	Z	 d dl
mZ d dlZddgZddZd	 Zd
 Zd Z	 	 	 ddZd ZddZ	 	 	 ddZdS )    N   )linear_sum_assignmentOptimizeResult)_check_unknown_options)check_random_statefaq2optc                     |i }|                                 }t          t          d}||vrt          d| d| d          t	          |                    dd                      ||         | |fi |}|S )a  
    Approximates solution to the quadratic assignment problem and
    the graph matching problem.

    Quadratic assignment solves problems of the following form:

    .. math::

        \min_P & \ {\ \text{trace}(A^T P B P^T)}\\
        \mbox{s.t. } & {P \ \epsilon \ \mathcal{P}}\\

    where :math:`\mathcal{P}` is the set of all permutation matrices,
    and :math:`A` and :math:`B` are square matrices.

    Graph matching tries to *maximize* the same objective function.
    This algorithm can be thought of as finding the alignment of the
    nodes of two graphs that minimizes the number of induced edge
    disagreements, or, in the case of weighted graphs, the sum of squared
    edge weight differences.

    Note that the quadratic assignment problem is NP-hard. The results given
    here are approximations and are not guaranteed to be optimal.


    Parameters
    ----------
    A : 2-D array, square
        The square matrix :math:`A` in the objective function above.

    B : 2-D array, square
        The square matrix :math:`B` in the objective function above.

    method :  str in {'faq', '2opt'} (default: 'faq')
        The algorithm used to solve the problem.
        :ref:`'faq' <optimize.qap-faq>` (default) and
        :ref:`'2opt' <optimize.qap-2opt>` are available.

    options : dict, optional
        A dictionary of solver options. All solvers support the following:

        maximize : bool (default: False)
            Maximizes the objective function if ``True``.

        partial_match : 2-D array of integers, optional (default: None)
            Fixes part of the matching. Also known as a "seed" [2]_.

            Each row of `partial_match` specifies a pair of matched nodes:
            node ``partial_match[i, 0]`` of `A` is matched to node
            ``partial_match[i, 1]`` of `B`. The array has shape ``(m, 2)``,
            where ``m`` is not greater than the number of nodes, :math:`n`.

        rng : `numpy.random.Generator`, optional
            Pseudorandom number generator state. When `rng` is None, a new
            `numpy.random.Generator` is created using entropy from the
            operating system. Types other than `numpy.random.Generator` are
            passed to `numpy.random.default_rng` to instantiate a ``Generator``.

            .. versionchanged:: 1.15.0
                As part of the `SPEC-007 <https://scientific-python.org/specs/spec-0007/>`_
                transition from use of `numpy.random.RandomState` to
                `numpy.random.Generator` is occurring. Supplying
                `np.random.RandomState` to this function will now emit a
                `DeprecationWarning`. In SciPy 1.17 its use will raise an exception.
                In addition relying on global state using `np.random.seed`
                will emit a `FutureWarning`. In SciPy 1.17 the global random number
                generator will no longer be used.
                Use of an int-like seed will raise a `FutureWarning`, in SciPy 1.17 it
                will be normalized via `np.random.default_rng` rather than
                `np.random.RandomState`.

        For method-specific options, see
        :func:`show_options('quadratic_assignment') <show_options>`.

    Returns
    -------
    res : OptimizeResult
        `OptimizeResult` containing the following fields.

        col_ind : 1-D array
            Column indices corresponding to the best permutation found of the
            nodes of `B`.
        fun : float
            The objective value of the solution.
        nit : int
            The number of iterations performed during optimization.

    Notes
    -----
    The default method :ref:`'faq' <optimize.qap-faq>` uses the Fast
    Approximate QAP algorithm [1]_; it typically offers the best combination of
    speed and accuracy.
    Method :ref:`'2opt' <optimize.qap-2opt>` can be computationally expensive,
    but may be a useful alternative, or it can be used to refine the solution
    returned by another method.

    References
    ----------
    .. [1] J.T. Vogelstein, J.M. Conroy, V. Lyzinski, L.J. Podrazik,
           S.G. Kratzer, E.T. Harley, D.E. Fishkind, R.J. Vogelstein, and
           C.E. Priebe, "Fast approximate quadratic programming for graph
           matching," PLOS one, vol. 10, no. 4, p. e0121002, 2015,
           :doi:`10.1371/journal.pone.0121002`

    .. [2] D. Fishkind, S. Adali, H. Patsolic, L. Meng, D. Singh, V. Lyzinski,
           C. Priebe, "Seeded graph matching", Pattern Recognit. 87 (2019):
           203-215, :doi:`10.1016/j.patcog.2018.09.014`

    .. [3] "2-opt," Wikipedia.
           https://en.wikipedia.org/wiki/2-opt

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.optimize import quadratic_assignment
    >>> rng = np.random.default_rng()
    >>> A = np.array([[0, 80, 150, 170], [80, 0, 130, 100],
    ...               [150, 130, 0, 120], [170, 100, 120, 0]])
    >>> B = np.array([[0, 5, 2, 7], [0, 0, 3, 8],
    ...               [0, 0, 0, 3], [0, 0, 0, 0]])
    >>> res = quadratic_assignment(A, B, options={'rng': rng})
    >>> print(res)
         fun: 3260
     col_ind: [0 3 2 1]
         nit: 9

    The see the relationship between the returned ``col_ind`` and ``fun``,
    use ``col_ind`` to form the best permutation matrix found, then evaluate
    the objective function :math:`f(P) = trace(A^T P B P^T )`.

    >>> perm = res['col_ind']
    >>> P = np.eye(len(A), dtype=int)[perm]
    >>> fun = np.trace(A.T @ P @ B @ P.T)
    >>> print(fun)
    3260

    Alternatively, to avoid constructing the permutation matrix explicitly,
    directly permute the rows and columns of the distance matrix.

    >>> fun = np.trace(A.T @ B[perm][:, perm])
    >>> print(fun)
    3260

    Although not guaranteed in general, ``quadratic_assignment`` happens to
    have found the globally optimal solution.

    >>> from itertools import permutations
    >>> perm_opt, fun_opt = None, np.inf
    >>> for perm in permutations([0, 1, 2, 3]):
    ...     perm = np.array(perm)
    ...     fun = np.trace(A.T @ B[perm][:, perm])
    ...     if fun < fun_opt:
    ...         fun_opt, perm_opt = fun, perm
    >>> print(np.array_equal(perm_opt, res['col_ind']))
    True

    Here is an example for which the default method,
    :ref:`'faq' <optimize.qap-faq>`, does not find the global optimum.

    >>> A = np.array([[0, 5, 8, 6], [5, 0, 5, 1],
    ...               [8, 5, 0, 2], [6, 1, 2, 0]])
    >>> B = np.array([[0, 1, 8, 4], [1, 0, 5, 2],
    ...               [8, 5, 0, 5], [4, 2, 5, 0]])
    >>> res = quadratic_assignment(A, B, options={'rng': rng})
    >>> print(res)
         fun: 178
     col_ind: [1 0 3 2]
         nit: 13

    If accuracy is important, consider using  :ref:`'2opt' <optimize.qap-2opt>`
    to refine the solution.

    >>> guess = np.array([np.arange(len(A)), res.col_ind]).T
    >>> res = quadratic_assignment(A, B, method="2opt",
    ...     options = {'rng': rng, 'partial_guess': guess})
    >>> print(res)
         fun: 176
     col_ind: [1 2 3 0]
         nit: 17

    N)r   r	   zmethod z must be in .rng)lower_quadratic_assignment_faq_quadratic_assignment_2opt
ValueError_spec007_transitionget)ABmethodoptionsmethodsress         S/var/www/html/test/jupyter/venv/lib/python3.11/site-packages/scipy/optimize/_qap.pyquadratic_assignmentr      s    l \\^^F/13 3GWA6AAwAAABBBE400111
'&/!Q
*
*'
*
*CJ    c                    t          | t          j        j                  rt	          j        dt          d           | | t          j        u r<t          j        j        j        j	        j
        t	          j        dt          d           t          | t          j        t          j        z            rt	          j        dt          d           d S d S )NzlUse of `RandomState` with `quadratic_assignment` is deprecated and will result in an exception in SciPy 1.17   )
stacklevelz~The NumPy global RNG was seeded by calling `np.random.seed`. From SciPy 1.17, this function will no longer use the global RNG.zThe behavior when the rng option is an integer is changing: the value will be normalized using np.random.default_rng beginning in SciPy 1.17, and the resulting Generator will be used to generate random numbers.)
isinstancenprandomRandomStatewarningswarnDeprecationWarningmtrand_rand_bit_generator	_seed_seqFutureWarningnumbersIntegralinteger)r   s    r   r   r      s    #ry,-- 
=		
 	
 	
 	
 
ry((I"1;CQ		
 	
 	
 	
 #w'"*455 
T 	
 	
 	
 	
 	
 	

 
r   c                 P    t          j        | ||         d d |f         z            S N)r    sum)r   r   perms      r   _calc_scorer2      s(    6!agaaag&&'''r   c                     t          j        |           } t          j        |          }|t          j        g g g          j        }t          j        |                              t
                    }d }| j        d         | j        d         k    rd}nV|j        d         |j        d         k    rd}n6| j        dk    s|j        dk    rd}n| j        |j        k    rd}n|j        d         | j        d         k    rd}n|j        d         dk    rd	}n|j        dk    rd
}n|dk                                     rd}n|t          |           k                                    rd}nt          t          |d d df                             t          |d d df                   k    rAt          t          |d d df                             t          |d d df                   k    sd}|t          |          | ||fS )Nr   r   z`A` must be squarez`B` must be squarer   z,`A` and `B` must have exactly two dimensionsz*`A` and `B` matrices must be of equal sizez>`partial_match` can have only as many seeds as there are nodesz%`partial_match` must have two columnsz0`partial_match` must have exactly two dimensionsz2`partial_match` must contain only positive indicesz9`partial_match` entries must be less than number of nodesz-`partial_match` column entries must be unique)r    
atleast_2darrayTastypeintshapendimanylensetr   )r   r   partial_matchmsgs       r   _common_input_validationr@      s   
aA
aA"b**,M-0077<<M
CwqzQWQZ"	
qwqz	!	!"	
1!<	
AG		:		Q	!'!*	,	,N		Q	1	$	$5		q	 	 @
!
	 	 	"	" >B
3q66
!	&	&	(	( >I#mAAAqD)**++s=A3F/G/GGG#mAAAqD)**++s=A3F/G/GGG=
ooar   F
barycenter   Q?c	                 
   t          |	           t          j        |          }t          | ||          \  } }}d}
t	          |t
                    r|dvrd}
n|dk    rd}
n|dk    rd}
|
t          |
          t          |          }t          |           }t          |          }||z
  }t	          |t
                    st          j
        |          }|j        ||fk    rd}
nl|dk                                     sRt          j        t          j        |d          d	          r)t          j        t          j        |d	          d	          sd
}
|
t          |
          nl|dk    rt          j        ||f          |z  }nL|dk    rFt          j        ||f          |z  }t!          |                    ||f                    }||z   dz  }|dk    s||k    r:t%          | ||ddd	f                   }|ddd	f         |dd}t'          |          S d	}|rd}t          j        t+          |          |ddd	f                   }|r|                    |          }t          j        t+          |          |dddf                   }t          j        |dddf         |g          }t          j        |ddd	f         |g          }t1          | |         dd|f         |          \  }}}}t1          ||         dd|f         |          \  }}}}||j        z  |j        |z  z   }|} t+          d	|d	z             D ]}!||| z  |j        z  z   |j        | z  |z  z   }"t5          |"|          \  }#}$t          j        |          |$         }%| |%z
  }&|&j        |z  |z                                  }'|&j        |j        z  |j        z                                  }(|j        |&z  })||&j        z  }*|)|j        |$         z                                  }+||*|$         z                                  },|)j        |*z                                  }-|'|(z   |+z   |,z   }.|-|z  dk    r!d|. d|-z  z  cxk    rd	k    rn n
|. d|-z  z  }/nt          j        d|.|-z   |z  g          }/|/| z  d	|/z
  |%z  z   }0t          j                            | |0z
            t          j        |          z  |k     r|0}  n|0} t5          | d          \  }#}1t          j        t          j         |          |1|z   f          }2t          j!        |tD                    }3||2         |3|<   t%          | ||3          }|3||!d}t'          |          S )aB  Solve the quadratic assignment problem (approximately).

    This function solves the Quadratic Assignment Problem (QAP) and the
    Graph Matching Problem (GMP) using the Fast Approximate QAP Algorithm
    (FAQ) [1]_.

    Quadratic assignment solves problems of the following form:

    .. math::

        \min_P & \ {\ \text{trace}(A^T P B P^T)}\\
        \mbox{s.t. } & {P \ \epsilon \ \mathcal{P}}\\

    where :math:`\mathcal{P}` is the set of all permutation matrices,
    and :math:`A` and :math:`B` are square matrices.

    Graph matching tries to *maximize* the same objective function.
    This algorithm can be thought of as finding the alignment of the
    nodes of two graphs that minimizes the number of induced edge
    disagreements, or, in the case of weighted graphs, the sum of squared
    edge weight differences.

    Note that the quadratic assignment problem is NP-hard. The results given
    here are approximations and are not guaranteed to be optimal.

    Parameters
    ----------
    A : 2-D array, square
        The square matrix :math:`A` in the objective function above.
    B : 2-D array, square
        The square matrix :math:`B` in the objective function above.
    method :  str in {'faq', '2opt'} (default: 'faq')
        The algorithm used to solve the problem. This is the method-specific
        documentation for 'faq'.
        :ref:`'2opt' <optimize.qap-2opt>` is also available.

    Options
    -------
    maximize : bool (default: False)
        Maximizes the objective function if ``True``.
    partial_match : 2-D array of integers, optional (default: None)
        Fixes part of the matching. Also known as a "seed" [2]_.

        Each row of `partial_match` specifies a pair of matched nodes:
        node ``partial_match[i, 0]`` of `A` is matched to node
        ``partial_match[i, 1]`` of `B`. The array has shape ``(m, 2)``, where
        ``m`` is not greater than the number of nodes, :math:`n`.

    rng : {None, int, `numpy.random.Generator`}, optional
        Pseudorandom number generator state. See `quadratic_assignment` for details.
    P0 : 2-D array, "barycenter", or "randomized" (default: "barycenter")
        Initial position. Must be a doubly-stochastic matrix [3]_.

        If the initial position is an array, it must be a doubly stochastic
        matrix of size :math:`m' \times m'` where :math:`m' = n - m`.

        If ``"barycenter"`` (default), the initial position is the barycenter
        of the Birkhoff polytope (the space of doubly stochastic matrices).
        This is a :math:`m' \times m'` matrix with all entries equal to
        :math:`1 / m'`.

        If ``"randomized"`` the initial search position is
        :math:`P_0 = (J + K) / 2`, where :math:`J` is the barycenter and
        :math:`K` is a random doubly stochastic matrix.
    shuffle_input : bool (default: False)
        Set to `True` to resolve degenerate gradients randomly. For
        non-degenerate gradients this option has no effect.
    maxiter : int, positive (default: 30)
        Integer specifying the max number of Frank-Wolfe iterations performed.
    tol : float (default: 0.03)
        Tolerance for termination. Frank-Wolfe iteration terminates when
        :math:`\frac{||P_{i}-P_{i+1}||_F}{\sqrt{m'}} \leq tol`,
        where :math:`i` is the iteration number.

    Returns
    -------
    res : OptimizeResult
        `OptimizeResult` containing the following fields.

        col_ind : 1-D array
            Column indices corresponding to the best permutation found of the
            nodes of `B`.
        fun : float
            The objective value of the solution.
        nit : int
            The number of Frank-Wolfe iterations performed.

    Notes
    -----
    The algorithm may be sensitive to the initial permutation matrix (or
    search "position") due to the possibility of several local minima
    within the feasible region. A barycenter initialization is more likely to
    result in a better solution than a single random initialization. However,
    calling ``quadratic_assignment`` several times with different random
    initializations may result in a better optimum at the cost of longer
    total execution time.

    Examples
    --------
    As mentioned above, a barycenter initialization often results in a better
    solution than a single random initialization.

    >>> from scipy.optimize import quadratic_assignment
    >>> import numpy as np
    >>> rng = np.random.default_rng()
    >>> n = 15
    >>> A = rng.random((n, n))
    >>> B = rng.random((n, n))
    >>> options = {"rng": rng}
    >>> res = quadratic_assignment(A, B, options=options)  # FAQ is default method
    >>> print(res.fun)
    47.797048706380636  # may vary

    >>> options = {"rng": rng, "P0": "randomized"}  # use randomized initialization
    >>> res = quadratic_assignment(A, B, options=options)
    >>> print(res.fun)
    47.37287069769966 # may vary

    However, consider running from several randomized initializations and
    keeping the best result.

    >>> res = min([quadratic_assignment(A, B, options=options)
    ...            for i in range(30)], key=lambda x: x.fun)
    >>> print(res.fun)
    46.55974835248574 # may vary

    The '2-opt' method can be used to attempt to refine the results.

    >>> options = {"partial_guess": np.array([np.arange(n), res.col_ind]).T, "rng": rng}
    >>> res = quadratic_assignment(A, B, method="2opt", options=options)
    >>> print(res.fun)
    46.55974835248574 # may vary

    References
    ----------
    .. [1] J.T. Vogelstein, J.M. Conroy, V. Lyzinski, L.J. Podrazik,
           S.G. Kratzer, E.T. Harley, D.E. Fishkind, R.J. Vogelstein, and
           C.E. Priebe, "Fast approximate quadratic programming for graph
           matching," PLOS one, vol. 10, no. 4, p. e0121002, 2015,
           :doi:`10.1371/journal.pone.0121002`

    .. [2] D. Fishkind, S. Adali, H. Patsolic, L. Meng, D. Singh, V. Lyzinski,
           C. Priebe, "Seeded graph matching", Pattern Recognit. 87 (2019):
           203-215, :doi:`10.1016/j.patcog.2018.09.014`

    .. [3] "Doubly stochastic Matrix," Wikipedia.
           https://en.wikipedia.org/wiki/Doubly_stochastic_matrix

    N>   rA   
randomizedzInvalid 'P0' parameter stringr   z$'maxiter' must be a positive integerz'tol' must be a positive floatz1`P0` matrix must have shape m' x m', where m'=n-maxisr   z%`P0` matrix must be doubly stochasticrA   rE   )sizer   col_indfunnit)maximizeTdtype)#r   operatorindexr@   r   strr   r   r<   r    r4   r9   r;   allcloser0   ones_doubly_stochasticuniformr2   r   	setdiff1drangepermutationconcatenate_split_matrixr6   r   eyeargminlinalgnormsqrtarangezerosr8   )4r   r   rN   r>   r   P0shuffle_inputmaxitertolunknown_optionsr?   nn_seedsn_unseedJKscorer   obj_func_scalar	nonseed_B	nonseed_Aperm_Aperm_BA11A12A21A22B11B12B21B22	const_sumPn_itergrad_fp_colsQRb21b12AR22BR22b22ab22babalphaP_i1colr1   unshuffled_perms4                                                       r   r   r     s   t ?+++nW%%G 31aGGAq-
C"c /r)EEE-	A4	.
oo
S
!
!CAA-  G7{H b# ]28(+++ECCAvllnn 	:BKr0B0B0BA$F$F 	:[!3!3!3Q77	:9C?S//! 	|		Wh)**X5	|		GXx())H4 s{{80D{EEFF!eq[ 	AvvAAq-1"566'1-eAFFc"""O U1XX}QQQT':;;I /OOI..	U1XX}QQQT':;;I^]111a40)<==F^]111a40)<==F 'qyF';WEECc3&qyF';WEECc3cececk)I
A719%% " "sQw.S@'(CCC4F8T" Ec	S %%''cesu$))++uqyQSysuT{"''))d4j %%''Vd]!!#It# _q  Q1"ac(%7%7%7%7a%7%7%7%7%7B!HEEIq1q5/"9:;;E qyAI?*9>>!d(##bgh&7&77#==AE #1t444FAs>29W--sW}=>>Dhq,,,O$TlOF1o..E%eF
C
CC#r   c                     | d |         | |d          }}|d d d |f         |d d |d f         |d d d |f         |d d |d f         fS r/    )Xri   upperr   s       r   r\   r\   $  sj    RaR5!ABB%5EBQB<qqq!""uuQQQU|U111abb5\AAr   MbP?c                    d}d|                      d          z  }d| |z  z  }| }t          |          D ]}t          j        |                     d          dz
            |k                                     rCt          j        |                     d          dz
            |k                                     r n#d|| z  z  }d| |z  z  }|d d d f         | z  |z  }|S )Ni  r   r   rF   )r0   rY   r    absall)r}   rg   max_itercrP_epsits          r   rV   rV   *  s     H	AEEqEMMA	QUAEHoo # #VEII1I%%)**S05577 			q	))A-..499;;	 EQKQK!!!T'
Q"Lr   c                    t          |           t          |          }t          | ||          \  } }}t          |           }|dk    s|j        d         |k    r:t          | ||dddf                   }|dddf         |dd}	t          |	          S |t          j        g g g          j	        }t          j
        |                              t                    }d}
|j        d         | j        d         k    rd}
n|j        d         dk    rd}
n|j        dk    rd}
n|dk                                     rd	}
n|t          |           k                                    rd
}
nt          t          |dddf                             t          |dddf                   k    rAt          t          |dddf                             t          |dddf                   k    sd}
|
t!          |
          d}|j        s|j        rt          j        |t&                    }t          j        |t&                    }t          j        |t&                    }t          j        |t&                    }t          j        |t                    }|j	        \  }}d||<   d||<   |||<   |j	        \  }}d||<   d||<   |||<   | | z  }| | z  }|                    t          j        |          |                   ||<   n'|                    t          j        |                    }t          | ||          }t          j        |          }|	||          }|rt,          j        nt,          j        }d}d}|sst3          j        |d          D ]Y\  }}|dz  }||         ||         c||<   ||<   t          | ||          } |||          r|} n||         ||         c||<   ||<   Zd}|s|||d}	t          |	          S )a  Solve the quadratic assignment problem (approximately).

    This function solves the Quadratic Assignment Problem (QAP) and the
    Graph Matching Problem (GMP) using the 2-opt algorithm [1]_.

    Quadratic assignment solves problems of the following form:

    .. math::

        \min_P & \ {\ \text{trace}(A^T P B P^T)}\\
        \mbox{s.t. } & {P \ \epsilon \ \mathcal{P}}\\

    where :math:`\mathcal{P}` is the set of all permutation matrices,
    and :math:`A` and :math:`B` are square matrices.

    Graph matching tries to *maximize* the same objective function.
    This algorithm can be thought of as finding the alignment of the
    nodes of two graphs that minimizes the number of induced edge
    disagreements, or, in the case of weighted graphs, the sum of squared
    edge weight differences.

    Note that the quadratic assignment problem is NP-hard. The results given
    here are approximations and are not guaranteed to be optimal.

    Parameters
    ----------
    A : 2-D array, square
        The square matrix :math:`A` in the objective function above.
    B : 2-D array, square
        The square matrix :math:`B` in the objective function above.
    method :  str in {'faq', '2opt'} (default: 'faq')
        The algorithm used to solve the problem. This is the method-specific
        documentation for '2opt'.
        :ref:`'faq' <optimize.qap-faq>` is also available.

    Options
    -------
    maximize : bool (default: False)
        Maximizes the objective function if ``True``.
    rng : {None, int, `numpy.random.Generator`}, optional
        Pseudorandom number generator state. See `quadratic_assignment` for details.
    partial_match : 2-D array of integers, optional (default: None)
        Fixes part of the matching. Also known as a "seed" [2]_.

        Each row of `partial_match` specifies a pair of matched nodes: node
        ``partial_match[i, 0]`` of `A` is matched to node
        ``partial_match[i, 1]`` of `B`. The array has shape ``(m, 2)``,
        where ``m`` is not greater than the number of nodes, :math:`n`.

        .. note::
             `partial_match` must be sorted by the first column.

    partial_guess : 2-D array of integers, optional (default: None)
        A guess for the matching between the two matrices. Unlike
        `partial_match`, `partial_guess` does not fix the indices; they are
        still free to be optimized.

        Each row of `partial_guess` specifies a pair of matched nodes: node
        ``partial_guess[i, 0]`` of `A` is matched to node
        ``partial_guess[i, 1]`` of `B`. The array has shape ``(m, 2)``,
        where ``m`` is not greater than the number of nodes, :math:`n`.

        .. note::
                `partial_guess` must be sorted by the first column.

    Returns
    -------
    res : OptimizeResult
        `OptimizeResult` containing the following fields.

        col_ind : 1-D array
            Column indices corresponding to the best permutation found of the
            nodes of `B`.
        fun : float
            The objective value of the solution.
        nit : int
            The number of iterations performed during optimization.

    Notes
    -----
    This is a greedy algorithm that works similarly to bubble sort: beginning
    with an initial permutation, it iteratively swaps pairs of indices to
    improve the objective function until no such improvements are possible.

    References
    ----------
    .. [1] "2-opt," Wikipedia.
           https://en.wikipedia.org/wiki/2-opt

    .. [2] D. Fishkind, S. Adali, H. Patsolic, L. Meng, D. Singh, V. Lyzinski,
           C. Priebe, "Seeded graph matching", Pattern Recognit. 87 (2019):
           203-215, https://doi.org/10.1016/j.patcog.2018.09.014

    r   Nr   rI   z@`partial_guess` can have only as many entries as there are nodesr   z%`partial_guess` must have two columnsz0`partial_guess` must have exactly two dimensionsz2`partial_guess` must contain only positive indicesz9`partial_guess` entries must be less than number of nodesz-`partial_guess` column entries must be uniquerO   TF)r   r   r@   r<   r9   r2   r   r    r5   r6   r4   r7   r8   r:   r;   r=   r   rH   rc   boolrZ   rb   rQ   gtlt	itertoolscombinations_with_replacement)r   r   rN   r   r>   partial_guessrh   Nrn   r   r?   
fixed_rows
guess_rows
guess_cols
fixed_colsr1   rgcgrfcfrandom_rowsrandom_cols
best_scorei_freebetterr~   doneijs                                r   r   r   B  sJ   D ?+++
S
!
!C21aGGAq-AAAvv$Q'1,,Aq-1"566'1-eAFFc""""b**,M-0077<<M
C1
**1		Q	1	$	$5		q	 	 @
!
	 	 	"	" >B
3q66
!	&	&	(	( >I#mAAAqD)**++s=A3F/G/GGG#mAAAqD)**++s=A3F/G/GGG=
ooJ -]/ - Xat,,,
Xat,,,
Xat,,,
Xat,,,
x%%%B
2
2Z B
2
2Z!kZK/!kZK/OOBIaLL,EFF[ry||,,Q4((JYq\\F$$5X[[(+FFD ;FAFF 
	 
	DAqaKF#AwQDGT!W1d++EveZ(( "
#AwQDGT!WWD   :f
=
=C#r   )r   N)FNNrA   FrB   rC   )r   )FNNN)numpyr    rQ   r#   r+    r   r   	_optimizer   scipy._lib._utilr   r   QUADRATIC_ASSIGNMENT_METHODSr   r   r2   r@   r   r\   rV   r   r   r   r   <module>r      s@          5 5 5 5 5 5 5 5 - - - - - - / / / / / /     %v A A A AH
 
 
4( ( (
" " "L GKLN"&K K K K\B B B   0 :>-1-1v v v v v vr   