Package: pylapy
PylapyWe provide a solver for the assignement problem with Hungarian algorithm (Jonker-Volgenant variants [1])The main class (pylapy.LapSolver) is a wrapper around different implementations you can find in python: lap, lapjv, scipy, lapsolver [2, 3, 4, 5].It unifies the functionality of each implementation and allows you to use the one which is the fastest
on your problem. Note that to solve the same problem, an implementation/method can be more than 10 times slower than an other one.It also helps you to handle non square matrices and setting a soft threshold on assignements (usually leads
to better performances than hard thresholding).Install$pipinstallpylapy
$# By default it does not install any backend solver$# You can either install by hand your favorite solver (scipy, lap, lapjv, lapsolver)$pipinstallpylapy[scipy]# or pylapy[lap] etc$# Note that some backend requires numpy to be installed correctly$# You may need to install numpy before$pipinstallnumpyGetting startedimportnumpyasnpimportpylapy# Simulate datan,m=(2000,2000)sparsity=0.5dist=np.random.uniform(0,1,(2000,2000))dist[np.random.uniform(0,1,(2000,2000))<sparsity]=np.inf# Create the solver and solvessolver=pylapy.LapSolver()# Choose the current most efficient method that is installed# solver = pylapy.LapSolver("scipy"|"lap"|"lapjv"|"lapsolver")  # You can choose which method you rather uselinks=solver.solve(dist)# Find the final costprint(dist[links[:,0],links[:,1]])BenchmarksWe provide several scripts (and corresponding plots) in thebenchmark/folder. They compare different implementations
and dist extensions (for non square matrix or soft thresholding). We have only tested them on a intel core i7 with Ubuntu 20.04
and python 3.10. Thus we do not guarantee that the choice we make by default are the fastest for you.TLDRLapjv seems to usually outperform other implementations (Up to 2 times faster). Lap and Scipy are also pretty fast and can sometimes be faster than Lapjv. Lapsolver is usually much slower and should be avoided.To handle soft thresholding and non-square matrices, we use by default the fastest options of our benchmark. This can be changed by settingLapSolver.cost_extensionandLapSolver.shape_extension.Handling non square matricesWith a rectangular assignment problem, you have to extend the distance matrix into a square one. There are multiple ways
to perform this shape extension. All yield the same final links and cost some are much faster/slower than others.We provide several shape extension functions and benchmark them. By default, the solver uses the fastest one for the implementation you use.
You can build your own or enforce another one by setting theLapSolver.shape_extensionattribute. Please have a look atshape_extensionmodule andbenchmark_shape_extensionscript for more details.According to our benchmark, we usesmallest_fill_inffor scipy [4] and smallest_fill_0 for other implementations. (Note that lap [2] provide its own implementation displayed asrefhere.)Handling soft thresholdingRather than applying hard thresholding and cut links that are above a thresholdeta, it is common and usually
better to assign a row or a column to "no one" with a costeta. This is done by adding "sink" rows and columns.
When a true row/column is linked to a "sink" column/row, it is considered non linked.Adding these sink nodes can also be done multiple ways resulting in equivalent links/cost but different run time.
We provide several cost extension functions and benchmark them. By default, the solver uses the expected fastest one
for the implementation you use. You can build your own extension or enforce another one by setting theLapSolver.cost_extensionattribute. Please have a look atcost_extensionmodule andbenchmark_cost_extensionscript for more details.It is less obvious to descriminate between the cost extension functions (Even more if you add sparsity: more plots inbenchmark/images/cost_extension). Nonetheless,
we decided to go withdiag_split_costfor lapsolver [5] androw_costfor other implementations that usually leads to the best performances.Choosing the implementationsFirst, some implementations are not available on some operating system or python version, our wrapper allows you to switch between implementations without
changing your code. It seems that for instance, lap is not available for python 3.6, lapjv is not available in macOs, etc.Also you want to choose the fastest one for your problem. We have compared all implementations on several cases (using sparsity, rectangular problems, and cost limit):It seems that lapjv is usually faster than other implementations. Scipy and lap are also pretty fast and can be faster than lapjv depending on your use case. Lapsolver is always outperformed and should be avoided.We have also testedlapmodfrom lap [2] for sparse matrices. It can sometimes be faster than other implementations but it is less stable and we do not add the support of this algorithm in the wrapper. (Note that for unfeasible problems, it yields a segfault).Warning: For rectangular matrices, lapjv seems to sometimes output a non-optimal cost (though very close to the optimal one)References[1] R. Jonker and A. Volgenant, "A Shortest Augmenting Path Algorithm for Dense and Sparse Linear Assignment Problems", Computing 38, 325-340 (1987)[2] "lap: Linear Assignment Problem solver",https://github.com/gatagat/lap[3] "lapjv: Linear Assignment Problem solver using Jonker-Volgenant algorithm",https://github.com/src-d/lapjv[4] "scipy: linear_sum_assignment",https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linear_sum_assignment.html#scipy.optimize.linear_sum_assignment[5] "py-lapsolver",https://github.com/cheind/py-lapsolver