scijit.optimize.linear_sum_assignment¶
- scijit.optimize.linear_sum_assignment(cost_matrix, maximize=False)¶
Solve the linear sum assignment problem.
Choose one entry from each row and each column so that the total is as small as possible: the least-cost way to give
nrjobs toncworkers, one each.- Parameters:
- cost_matrixarray_like, shape (nr, nc)
Cost matrix. From the interpreter, anything numpy converts to a 2-D numeric array: a list of lists, a tuple of tuples, an integer or boolean array, a Fortran-ordered array, a strided view. A masked array contributes its data and its mask is ignored. Inside
@njitit has to be a 2-D array already, of any numeric dtype; strided and Fortran-ordered arrays are accepted there too. An empty matrix is legal and yields two empty index arrays.- maximizebool, optional
If True maximize the total instead of minimizing it. Default False. Implemented by negating the matrix.
- Returns:
- (row_ind, col_ind)two int64 arrays of length
min(nr, nc) The assignment, as one entry per chosen row and column.
row_indis sorted ascending, andcost_matrix[row_ind, col_ind].sum()is its total cost.
- (row_ind, col_ind)two int64 arrays of length
- Raises:
- ValueError
If cost_matrix has a rank other than 2 once converted; if it holds a NaN or a
-inf, checked after the optional negation for maximize; or if no finite perfect matching of the short side exists.- TypeError
If an array is passed whose dtype does not cast to float64 under numpy’s
'safe'rule, such as a complex, object or string array.
See also
scipy.optimize.linear_sum_assignmentThe scipy routine this mirrors.
Notes
Solved by the modified Jonker-Volgenant shortest augmenting path algorithm. The tie-breaking rule is fixed so the index pairs are determined, not only the total cost: the work list is filled in reverse order, so a constant cost matrix returns the identity assignment, and among equal shortest-path costs a candidate that opens a new unmatched column wins.
Rank and container are compile-time properties of an argument, so inside
@njita rank other than 2 is aTypingErrorwhere the interpreter raisesValueError, and a list or a tuple is aTypingErrorwhere the interpreter converts it. The rank refusal carries the rank it was given; the container refusal names cost_matrix and the spelling to use instead.No module state, no callback and no globals, so many independent assignment problems can be solved concurrently inside a
numba.prangeloop.Examples
>>> import numpy as np >>> from numba import njit >>> from scijit.optimize import linear_sum_assignment >>> cost = np.array([[4.0, 1.0, 3.0], ... [2.0, 0.0, 5.0], ... [3.0, 2.0, 2.0]]) >>> @njit ... def run(): ... return linear_sum_assignment(cost) >>> row_ind, col_ind = run() >>> row_ind array([0, 1, 2]) >>> col_ind array([1, 0, 2])
From the interpreter the same problem can be written as a list of lists:
>>> linear_sum_assignment([[4, 1, 3], [2, 0, 5], [3, 2, 2]])[1] array([1, 0, 2])
numba rejects indexing one array with two index arrays, so the total is summed in a loop rather than as
cost[row_ind, col_ind].sum():>>> @njit ... def total(): ... r, c = linear_sum_assignment(cost) ... s = 0.0 ... for k in range(r.size): ... s += cost[r[k], c[k]] ... return s >>> total() 5.0