← Back
Editing: _qap.cpython-311.pyc
� d�c l � � � d dl Zd dlZddlmZmZ ddlmZ d dlm Z d dl Z ddgZdd�Zd � Z d � Z dd�Zd� Zdd�Z dd�ZdS )� N� )�linear_sum_assignment�OptimizeResult)�_check_unknown_options)�check_random_state�faq�2optc � � |�i }|� � � }t t d�}||vrt d|� d|� d�� � � || | |fi |��}|S )ay 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 : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional If `seed` is None (or `np.random`), the `numpy.random.RandomState` singleton is used. If `seed` is an int, a new ``RandomState`` instance is used, seeded with `seed`. If `seed` is already a ``Generator`` or ``RandomState`` instance then that instance is used. 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 >>> 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) >>> 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) >>> 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 = {'partial_guess': guess}) >>> print(res) fun: 176 col_ind: [1 2 3 0] nit: 17 N)r r zmethod z must be in �.)�lower�_quadratic_assignment_faq�_quadratic_assignment_2opt� ValueError)�A�B�method�options�methods�ress �5/usr/lib/python3/dist-packages/scipy/optimize/_qap.py�quadratic_assignmentr s} � �X ���� �\�\�^�^�F�/�1�3� 3�G� �W����A�6�A�A�w�A�A�A�B�B�B� �'�&�/�!�Q� *� *�'� *� *�C��J� c �P � t j | || d d �|f z � � S �N)�np�sum)r r �perms r �_calc_scorer � s( � � �6�!�a��g�a�a�a��g�&�&�'�'�'r c � � t j | � � } t j |� � }|�t j g g g� � j }t j |� � � t � � }d }| j d | j d k rd}�nV|j d |j d k rd}�n6| j dk s|j dk rd}�n| j |j k rd}�n|j d | j d k rd}n�|j d dk rd }n�|j dk rd }n�|dk � � � rd}n�|t | � � k � � � rd}n�t t |d d �df � � � � t |d d �df � � k rAt t |d d �df � � � � t |d d �df � � k sd }|�t |� � �| ||fS )Nr r z`A` must be squarez`B` must be square� 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��w�q�z�Q�W�Q�Z���"��� ����q�w�q�z� !� !�"��� ��1�����!���<��� ��A�G� � �:��� � �Q� �!�'�!�*� ,� ,�N��� � �Q� �1� $� $�5��� � �q� � �@��� �!� � � � "� "� >�B��� �3�q�6�6� !� &� &� (� (� >�I����#�m�A�A�A�q�D�)�*�*�+�+�s�=����A��3F�/G�/G�G�G��#�m�A�A�A�q�D�)�*�*�+�+�s�=����A��3F�/G�/G�G�G�=�� ����o�o���a���r F� barycenter� ���Q��?c �� � t | � � t j |� � }t | ||� � \ } }}d} t |t � � r|dvrd} n|dk rd} n|dk rd} | �t | � � �t |� � }t | � � }t |� � }||z } t |t � � s�t j |� � }|j | | fk rd} nl|dk � � � sRt j t j |d�� � d � � r)t j t j |d �� � d � � sd } | �t | � � �nl|dk rt j | | f� � | z }nL|dk rFt j | | f� � | z }t! |� | | f� � � � � }||z dz }|dk s||k r:t% | ||dd�d f � � }|dd�d f |dd�}t'