{ } Raw JSON

bundles / scipy latest / scipy / optimize / _qap / quadratic_assignment

function

scipy.optimize._qap:quadratic_assignment

source: /scipy/optimize/_qap.py :14

Signature

def   quadratic_assignment ( A B method = faq options = None )

Summary

Approximates solution to the quadratic assignment problem and the graph matching problem.

Extended Summary

Quadratic assignment solves problems of the following form:

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

where is the set of all permutation matrices, and and 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 in the objective function above.

B : 2-D array, square

The square matrix in the objective function above.

method : str in {'faq', '2opt'} (default: 'faq')

The algorithm used to solve the problem. 'faq' <optimize.qap-faq> (default) and '2opt' <optimize.qap-2opt> are available.

options : dict, optional

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

maximize

maximize

partial_match

partial_match

rng

rng

For method-specific options, see show_options('quadratic_assignment').

Returns

res : OptimizeResult

OptimizeResult containing the following fields.

col_ind

col_ind

fun

fun

nit

nit

Notes

The default method 'faq' <optimize.qap-faq> uses the Fast Approximate QAP algorithm [1]; it typically offers the best combination of speed and accuracy. Method '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.

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)
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)
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)
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']))
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)
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)

Aliases

  • scipy.optimize.quadratic_assignment

Referenced by