Assignment
Linear assignment problem solvers — Jonker-Volgenant, Hungarian (Munkres, Lawler), and Greedy — registered as PyTorch custom ops.
The assignment family solves the linear assignment problem (LAP): given a cost matrix
C of shape (N, M), find the one-to-one mapping from rows to columns that minimises the
total cost. The problem is also called bipartite matching — pairing items from one set (rows)
with items from another set (columns) so each item appears in at most one pair — or the
weighted assignment problem.
torchmatch exposes the assignment solvers through a single dispatcher:
import torchmatch
# 2-D cost: single problem, CPU or CUDA
row_to_col = torchmatch.assignment.solve(cost)
# 3-D cost: batch of problems, CPU or CUDA
row_to_col = torchmatch.assignment.solve(costs) # shape (B, N)
The dispatcher (Backend.AUTO) picks the fastest registered op for the device, shape, and
cost structure. Direct op handles (e.g., torchmatch.assignment.ops.jonker_dense) are also
available when you need to fix a particular algorithm — for benchmarking or to avoid the
auto-selection overhead.
When assignment is the right tool
Use assignment when you need a hard, one-to-one matching between two sets of items of known, finite size. Common situations:
- Object tracking: match each detected bounding box this frame to one track from the previous frame.
- Set-prediction losses: match each model prediction to one ground-truth target so you can compute a per-pair loss — as used in transformer-based object detectors like DETR.
- Cluster evaluation: when two clustering algorithms assign different integer labels to the same groups, find the label mapping that maximises the overlap before comparing them.
- Replacing SciPy:
torchmatch.assignment.solveis a drop-in replacement forscipy.optimize.linear_sum_assignmentthat accepts and returns PyTorch tensors directly, without leaving the GPU or converting to NumPy arrays.
If you are comparing probability distributions, working with point clouds, or want a fractional (probabilistic) rather than hard one-to-one matching, look at Transport instead.
Three solver families
| Family | Ops | Hardware |
|---|---|---|
| Jonker-Volgenant (successive-shortest-path) | jonker_scalar, jonker_dense, jonker_compact and their batched variants | CPU (AVX2 SIMD); CUDA for jonker_dense_batch |
| Hungarian algorithm family | munkres, lawler, hybrid | CUDA only |
| Pure-Python heuristics | greedy, auction_assignment | CPU and CUDA (no compiled extension) |
auction_assignment (Bertsekas' auction algorithm) returns a
(matches, unmatched_rows, unmatched_cols) triple and is not wired into
solve. Use it when you need an optimal or near-optimal match without the
compiled extension. greedy is a single-pass O(N²) heuristic suited for
warm-starts or profiling.
The Algorithms page explains the mathematical differences. The Choosing guide maps problem characteristics to the right op.
In this section
- Quickstart — install, import, and run your first assignment
- Tracking tutorial — end-to-end batched tracking with
jonker_dense_batch - Algorithms — how Jonker-Volgenant, Munkres, and Lawler work and how they differ
- Reference — full signature for every op
- Choosing — benchmark-backed decision tree
- Tutorials — deeper notebook-based walkthroughs, from first principles through object tracking
See also the history of applications — tracking, DETR losses, cluster evaluation, and more.
Algorithms
The two solver families torchmatch provides — linear assignment (one-to-one matching) and optimal transport (soft, many-to-many matching) — each with quickstarts, algorithm notes, and a full op reference.
Quickstart
Solve one cost matrix on CPU with each JV variant, covering square, rectangular, forbidden-edge, NaN, and empty inputs.