[{"data":1,"prerenderedAt":3008},["ShallowReactive",2],{"navigation":3,"\u002Falgorithms\u002Fassignment\u002Ftutorials\u002Ftracking":174,"\u002Falgorithms\u002Fassignment\u002Ftutorials\u002Ftracking-surround":2607,"docyard:bib:\u002Freferences.bib":2612},[4,8,101,165,170],{"title":5,"path":6,"stem":7},"Getting started","\u002Fgetting-started","1.getting-started",{"title":9,"path":10,"stem":11,"children":12},"Algorithms","\u002Falgorithms","2.algorithms",[13,15,58],{"title":9,"path":10,"stem":14},"2.algorithms\u002Findex",{"title":16,"path":17,"stem":18,"children":19},"Assignment","\u002Falgorithms\u002Fassignment","2.algorithms\u002F1.assignment\u002Findex",[20,21,25,29,32,36,40],{"title":16,"path":17,"stem":18},{"title":22,"path":23,"stem":24},"Quickstart","\u002Falgorithms\u002Fassignment\u002Fquickstart","2.algorithms\u002F1.assignment\u002F1.quickstart",{"title":26,"path":27,"stem":28},"Tracking","\u002Falgorithms\u002Fassignment\u002Ftracking","2.algorithms\u002F1.assignment\u002F2.tracking",{"title":9,"path":30,"stem":31},"\u002Falgorithms\u002Fassignment\u002Falgorithms","2.algorithms\u002F1.assignment\u002F3.algorithms",{"title":33,"path":34,"stem":35},"Reference","\u002Falgorithms\u002Fassignment\u002Freference","2.algorithms\u002F1.assignment\u002F4.reference",{"title":37,"path":38,"stem":39},"Choosing","\u002Falgorithms\u002Fassignment\u002Fchoosing","2.algorithms\u002F1.assignment\u002F5.choosing",{"title":41,"path":42,"stem":43,"children":44},"Tutorials","\u002Falgorithms\u002Fassignment\u002Ftutorials","2.algorithms\u002F1.assignment\u002F6.tutorials\u002Findex",[45,46,50,54],{"title":41,"path":42,"stem":43},{"title":47,"path":48,"stem":49},"Fundamentals","\u002Falgorithms\u002Fassignment\u002Ftutorials\u002Ffundamentals","2.algorithms\u002F1.assignment\u002F6.tutorials\u002F1.fundamentals",{"title":51,"path":52,"stem":53},"Backends","\u002Falgorithms\u002Fassignment\u002Ftutorials\u002Fbackends","2.algorithms\u002F1.assignment\u002F6.tutorials\u002F2.backends",{"title":55,"path":56,"stem":57},"Object tracking","\u002Falgorithms\u002Fassignment\u002Ftutorials\u002Ftracking","2.algorithms\u002F1.assignment\u002F6.tutorials\u002F3.tracking",{"title":59,"path":60,"stem":61,"children":62},"Transport","\u002Falgorithms\u002Ftransport","2.algorithms\u002F2.transport\u002Findex",[63,64,67,71,74,77,80,84],{"title":59,"path":60,"stem":61},{"title":22,"path":65,"stem":66},"\u002Falgorithms\u002Ftransport\u002Fquickstart","2.algorithms\u002F2.transport\u002F1.quickstart",{"title":68,"path":69,"stem":70},"Point-cloud tutorial","\u002Falgorithms\u002Ftransport\u002Fpoint-clouds","2.algorithms\u002F2.transport\u002F2.point-clouds",{"title":9,"path":72,"stem":73},"\u002Falgorithms\u002Ftransport\u002Falgorithms","2.algorithms\u002F2.transport\u002F3.algorithms",{"title":33,"path":75,"stem":76},"\u002Falgorithms\u002Ftransport\u002Freference","2.algorithms\u002F2.transport\u002F4.reference",{"title":37,"path":78,"stem":79},"\u002Falgorithms\u002Ftransport\u002Fchoosing","2.algorithms\u002F2.transport\u002F5.choosing",{"title":81,"path":82,"stem":83},"Building","\u002Falgorithms\u002Ftransport\u002Fbuilding","2.algorithms\u002F2.transport\u002F6.building",{"title":41,"path":85,"stem":86,"children":87},"\u002Falgorithms\u002Ftransport\u002Ftutorials","2.algorithms\u002F2.transport\u002F7.tutorials\u002Findex",[88,89,93,97],{"title":41,"path":85,"stem":86},{"title":90,"path":91,"stem":92},"Optimal transport","\u002Falgorithms\u002Ftransport\u002Ftutorials\u002Foptimal-transport","2.algorithms\u002F2.transport\u002F7.tutorials\u002F1.optimal-transport",{"title":94,"path":95,"stem":96},"Sinkhorn","\u002Falgorithms\u002Ftransport\u002Ftutorials\u002Fsinkhorn","2.algorithms\u002F2.transport\u002F7.tutorials\u002F2.sinkhorn",{"title":98,"path":99,"stem":100},"Point clouds","\u002Falgorithms\u002Ftransport\u002Ftutorials\u002Fpoint-clouds","2.algorithms\u002F2.transport\u002F7.tutorials\u002F3.point-clouds",{"title":102,"path":103,"stem":104,"children":105},"Resources","\u002Fresources","3.resources",[106,108,147,151,155],{"title":102,"path":103,"stem":107},"3.resources\u002Findex",{"title":41,"path":109,"stem":110,"children":111},"\u002Fresources\u002Ftutorials","3.resources\u002F1.tutorials\u002Findex",[112,113,131],{"title":41,"path":109,"stem":110},{"title":16,"path":114,"stem":115,"children":116,"page":130},"\u002Fresources\u002Ftutorials\u002Fassignment","3.resources\u002F1.tutorials\u002Fassignment",[117,122,126],{"title":118,"path":119,"stem":120,"icon":121},"Tutorial 1 — The Assignment Problem","\u002Fresources\u002Ftutorials\u002Fassignment\u002F01_the_assignment_problem","3.resources\u002F1.tutorials\u002Fassignment\u002F01_the_assignment_problem","i-lucide-notebook",{"title":123,"path":124,"stem":125,"icon":121},"Tutorial 2 — Backends and Batching","\u002Fresources\u002Ftutorials\u002Fassignment\u002F02_backends_and_batching","3.resources\u002F1.tutorials\u002Fassignment\u002F02_backends_and_batching",{"title":127,"path":128,"stem":129,"icon":121},"Tutorial 3 — Object Tracking with the Assignment Problem","\u002Fresources\u002Ftutorials\u002Fassignment\u002F03_object_tracking","3.resources\u002F1.tutorials\u002Fassignment\u002F03_object_tracking",false,{"title":59,"path":132,"stem":133,"children":134,"page":130},"\u002Fresources\u002Ftutorials\u002Ftransport","3.resources\u002F1.tutorials\u002Ftransport",[135,139,143],{"title":136,"path":137,"stem":138,"icon":121},"Tutorial 1 — What Is Optimal Transport?","\u002Fresources\u002Ftutorials\u002Ftransport\u002F01_optimal_transport","3.resources\u002F1.tutorials\u002Ftransport\u002F01_optimal_transport",{"title":140,"path":141,"stem":142,"icon":121},"Tutorial 2 — The Sinkhorn Algorithm","\u002Fresources\u002Ftutorials\u002Ftransport\u002F02_sinkhorn_algorithm","3.resources\u002F1.tutorials\u002Ftransport\u002F02_sinkhorn_algorithm",{"title":144,"path":145,"stem":146,"icon":121},"Tutorial 3 — Point-Cloud OT and Shape Learning","\u002Fresources\u002Ftutorials\u002Ftransport\u002F03_point_clouds","3.resources\u002F1.tutorials\u002Ftransport\u002F03_point_clouds",{"title":148,"path":149,"stem":150},"Assignment applications","\u002Fresources\u002Fassignment-applications","3.resources\u002F2.assignment-applications",{"title":152,"path":153,"stem":154},"Transport applications","\u002Fresources\u002Ftransport-applications","3.resources\u002F3.transport-applications",{"title":156,"path":157,"stem":158,"children":159},"Benchmarks","\u002Fresources\u002Fbenchmarks","3.resources\u002F4.benchmarks\u002Findex",[160,161],{"title":156,"path":157,"stem":158},{"title":162,"path":163,"stem":164},"Contributing benchmarks","\u002Fresources\u002Fbenchmarks\u002Fcontributing","3.resources\u002F4.benchmarks\u002Fcontributing",{"title":166,"path":167,"stem":168,"icon":169},"API Reference","\u002Fapi","4.api","i-lucide-package",{"title":171,"path":172,"stem":173},"References","\u002Freferences","5.references",{"id":175,"title":176,"api":177,"body":178,"description":2601,"extension":2602,"links":177,"meta":2603,"navigation":2604,"path":56,"seo":2605,"stem":57,"__hash__":2606},"docs\u002F2.algorithms\u002F1.assignment\u002F6.tutorials\u002F3.tracking.md","Object Tracking with LAP",null,{"type":179,"value":180,"toc":2591},"minimark",[181,186,190,206,210,218,228,231,238,671,675,678,684,973,985,989,992,1008,1055,1058,2213,2217,2227,2230,2407,2411,2424,2531,2547,2551,2567,2571,2584,2587],[182,183,185],"h2",{"id":184},"the-multi-object-tracking-problem","The multi-object tracking problem",[187,188,189],"p",{},"A multi-object tracker receives a stream of detections from a detector (bounding boxes output by something like YOLO or DETR) and must maintain identity-consistent tracks across frames. At each frame, it answers the question: which detection corresponds to which existing track?",[187,191,192,193,197,198,201,202,205],{},"This is an assignment problem. Rows are the existing tracks (objects being followed), columns are the new detections, and the cost at ",[194,195,196],"code",{},"[i, j]"," measures how dissimilar track ",[194,199,200],{},"i"," and detection ",[194,203,204],{},"j"," appear. The solver finds the minimum-cost one-to-one matching.",[182,207,209],{"id":208},"intersection-over-union","Intersection over union",[187,211,212,213,217],{},"The standard similarity measure for axis-aligned bounding boxes is ",[214,215,216],"strong",{},"intersection over union"," (IoU). Given two boxes, compute the area of their overlap and divide by the area of their union:",[219,220,225],"pre",{"className":221,"code":223,"language":224},[222],"language-text","IoU(A, B) = area(A ∩ B) \u002F area(A ∪ B)\n","text",[194,226,223],{"__ignoreMap":227},"",[187,229,230],{},"IoU is 1 when the boxes coincide exactly, and 0 when they do not overlap at all. It is scale-invariant: a small box and a large box with identical overlap fraction produce the same IoU as two large boxes with the same fraction.",[187,232,233,234,237],{},"For the cost matrix, flip the sign: ",[194,235,236],{},"C[i, j] = 1 - IoU(track_i, det_j)",". A cost of 0 means a perfect match; a cost close to 1 means the boxes barely overlap.",[219,239,243],{"className":240,"code":241,"language":242,"meta":227,"style":227},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","import torch\n\ndef box_iou(boxes_a: torch.Tensor, boxes_b: torch.Tensor) -> torch.Tensor:\n    \"\"\"\n    Compute pairwise IoU between two sets of boxes.\n\n    Args:\n        boxes_a: (N, 4) in x1, y1, x2, y2 format\n        boxes_b: (M, 4) in x1, y1, x2, y2 format\n\n    Returns:\n        (N, M) IoU matrix\n    \"\"\"\n    lo = torch.maximum(boxes_a[:, None, :2], boxes_b[None, :, :2])\n    hi = torch.minimum(boxes_a[:, None, 2:], boxes_b[None, :, 2:])\n    inter = (hi - lo).clamp_min(0).prod(-1)\n    area_a = (boxes_a[:, 2:] - boxes_a[:, :2]).prod(-1)[:, None]\n    area_b = (boxes_b[:, 2:] - boxes_b[:, :2]).prod(-1)[None, :]\n    return inter \u002F (area_a + area_b - inter + 1e-9)\n","python",[194,244,245,258,265,326,333,340,345,351,357,363,368,374,380,385,446,492,538,588,637],{"__ignoreMap":227},[246,247,250,254],"span",{"class":248,"line":249},"line",1,[246,251,253],{"class":252},"sVHd0","import",[246,255,257],{"class":256},"su5hD"," torch\n",[246,259,261],{"class":248,"line":260},2,[246,262,264],{"emptyLinePlaceholder":263},true,"\n",[246,266,268,272,276,280,284,287,290,293,297,300,303,305,307,309,311,314,317,319,321,323],{"class":248,"line":267},3,[246,269,271],{"class":270},"sbsja","def",[246,273,275],{"class":274},"sGLFI"," box_iou",[246,277,279],{"class":278},"sP7_E","(",[246,281,283],{"class":282},"sFwrP","boxes_a",[246,285,286],{"class":278},":",[246,288,289],{"class":256}," torch",[246,291,292],{"class":278},".",[246,294,296],{"class":295},"skxfh","Tensor",[246,298,299],{"class":278},",",[246,301,302],{"class":282}," boxes_b",[246,304,286],{"class":278},[246,306,289],{"class":256},[246,308,292],{"class":278},[246,310,296],{"class":295},[246,312,313],{"class":278},")",[246,315,316],{"class":278}," ->",[246,318,289],{"class":256},[246,320,292],{"class":278},[246,322,296],{"class":295},[246,324,325],{"class":278},":\n",[246,327,329],{"class":248,"line":328},4,[246,330,332],{"class":331},"s2W-s","    \"\"\"\n",[246,334,336],{"class":248,"line":335},5,[246,337,339],{"class":338},"sithA","    Compute pairwise IoU between two sets of boxes.\n",[246,341,343],{"class":248,"line":342},6,[246,344,264],{"emptyLinePlaceholder":263},[246,346,348],{"class":248,"line":347},7,[246,349,350],{"class":338},"    Args:\n",[246,352,354],{"class":248,"line":353},8,[246,355,356],{"class":338},"        boxes_a: (N, 4) in x1, y1, x2, y2 format\n",[246,358,360],{"class":248,"line":359},9,[246,361,362],{"class":338},"        boxes_b: (M, 4) in x1, y1, x2, y2 format\n",[246,364,366],{"class":248,"line":365},10,[246,367,264],{"emptyLinePlaceholder":263},[246,369,371],{"class":248,"line":370},11,[246,372,373],{"class":338},"    Returns:\n",[246,375,377],{"class":248,"line":376},12,[246,378,379],{"class":338},"        (N, M) IoU matrix\n",[246,381,383],{"class":248,"line":382},13,[246,384,332],{"class":331},[246,386,388,391,395,397,399,403,405,407,410,414,416,419,423,426,428,431,434,436,439,441,443],{"class":248,"line":387},14,[246,389,390],{"class":256},"    lo ",[246,392,394],{"class":393},"smGrS","=",[246,396,289],{"class":256},[246,398,292],{"class":278},[246,400,402],{"class":401},"slqww","maximum",[246,404,279],{"class":278},[246,406,283],{"class":401},[246,408,409],{"class":278},"[:,",[246,411,413],{"class":412},"s39Yj"," None",[246,415,299],{"class":278},[246,417,418],{"class":278}," :",[246,420,422],{"class":421},"srdBf","2",[246,424,425],{"class":278},"],",[246,427,302],{"class":401},[246,429,430],{"class":278},"[",[246,432,433],{"class":412},"None",[246,435,299],{"class":278},[246,437,438],{"class":278}," :,",[246,440,418],{"class":278},[246,442,422],{"class":421},[246,444,445],{"class":278},"])\n",[246,447,449,452,454,456,458,461,463,465,467,469,471,474,477,479,481,483,485,487,489],{"class":248,"line":448},15,[246,450,451],{"class":256},"    hi ",[246,453,394],{"class":393},[246,455,289],{"class":256},[246,457,292],{"class":278},[246,459,460],{"class":401},"minimum",[246,462,279],{"class":278},[246,464,283],{"class":401},[246,466,409],{"class":278},[246,468,413],{"class":412},[246,470,299],{"class":278},[246,472,473],{"class":421}," 2",[246,475,476],{"class":278},":],",[246,478,302],{"class":401},[246,480,430],{"class":278},[246,482,433],{"class":412},[246,484,299],{"class":278},[246,486,438],{"class":278},[246,488,473],{"class":421},[246,490,491],{"class":278},":])\n",[246,493,495,498,500,503,506,509,512,515,518,520,523,525,528,530,532,535],{"class":248,"line":494},16,[246,496,497],{"class":256},"    inter ",[246,499,394],{"class":393},[246,501,502],{"class":278}," (",[246,504,505],{"class":256},"hi ",[246,507,508],{"class":393},"-",[246,510,511],{"class":256}," lo",[246,513,514],{"class":278},").",[246,516,517],{"class":401},"clamp_min",[246,519,279],{"class":278},[246,521,522],{"class":421},"0",[246,524,514],{"class":278},[246,526,527],{"class":401},"prod",[246,529,279],{"class":278},[246,531,508],{"class":393},[246,533,534],{"class":421},"1",[246,536,537],{"class":278},")\n",[246,539,541,544,546,548,550,552,554,557,560,563,565,567,569,572,574,576,578,580,583,585],{"class":248,"line":540},17,[246,542,543],{"class":256},"    area_a ",[246,545,394],{"class":393},[246,547,502],{"class":278},[246,549,283],{"class":256},[246,551,409],{"class":278},[246,553,473],{"class":421},[246,555,556],{"class":278},":]",[246,558,559],{"class":393}," -",[246,561,562],{"class":256}," boxes_a",[246,564,409],{"class":278},[246,566,418],{"class":278},[246,568,422],{"class":421},[246,570,571],{"class":278},"]).",[246,573,527],{"class":401},[246,575,279],{"class":278},[246,577,508],{"class":393},[246,579,534],{"class":421},[246,581,582],{"class":278},")[:,",[246,584,413],{"class":412},[246,586,587],{"class":278},"]\n",[246,589,591,594,596,598,601,603,605,607,609,611,613,615,617,619,621,623,625,627,630,632,634],{"class":248,"line":590},18,[246,592,593],{"class":256},"    area_b ",[246,595,394],{"class":393},[246,597,502],{"class":278},[246,599,600],{"class":256},"boxes_b",[246,602,409],{"class":278},[246,604,473],{"class":421},[246,606,556],{"class":278},[246,608,559],{"class":393},[246,610,302],{"class":256},[246,612,409],{"class":278},[246,614,418],{"class":278},[246,616,422],{"class":421},[246,618,571],{"class":278},[246,620,527],{"class":401},[246,622,279],{"class":278},[246,624,508],{"class":393},[246,626,534],{"class":421},[246,628,629],{"class":278},")[",[246,631,433],{"class":412},[246,633,299],{"class":278},[246,635,636],{"class":278}," :]\n",[246,638,640,643,646,649,651,654,657,660,662,664,666,669],{"class":248,"line":639},19,[246,641,642],{"class":252},"    return",[246,644,645],{"class":256}," inter ",[246,647,648],{"class":393},"\u002F",[246,650,502],{"class":278},[246,652,653],{"class":256},"area_a ",[246,655,656],{"class":393},"+",[246,658,659],{"class":256}," area_b ",[246,661,508],{"class":393},[246,663,645],{"class":256},[246,665,656],{"class":393},[246,667,668],{"class":421}," 1e-9",[246,670,537],{"class":278},[182,672,674],{"id":673},"gating-ruling-out-implausible-pairs","Gating: ruling out implausible pairs",[187,676,677],{},"Before solving, it is good practice to forbid pairs that are geometrically implausible. A track whose predicted box is in the top-left corner of the frame should not be matched to a detection in the bottom-right, even if it happens to have the lowest cost.",[187,679,680,681,286],{},"Set the cost for any pair whose centroid distance exceeds a threshold to ",[194,682,683],{},"+inf",[219,685,687],{"className":240,"code":686,"language":242,"meta":227,"style":227},"def iou_cost_gated(pred_boxes: torch.Tensor,\n                   det_boxes: torch.Tensor,\n                   gate: float = 0.5) -> torch.Tensor:\n    \"\"\"(1 - IoU) cost matrix with centroid-distance gating.\"\"\"\n    cost = 1.0 - box_iou(pred_boxes, det_boxes)\n    centers_pred = (pred_boxes[:, None, :2] + pred_boxes[:, None, 2:]) \u002F 2\n    centers_det  = (det_boxes[None, :, :2]  + det_boxes[None, :, 2:])  \u002F 2\n    dist = (centers_pred - centers_det).norm(dim=-1)\n    cost = cost.masked_fill(dist > gate, float(\"inf\"))\n    return cost\n",[194,688,689,712,727,756,767,792,839,887,922,966],{"__ignoreMap":227},[246,690,691,693,696,698,701,703,705,707,709],{"class":248,"line":249},[246,692,271],{"class":270},[246,694,695],{"class":274}," iou_cost_gated",[246,697,279],{"class":278},[246,699,700],{"class":282},"pred_boxes",[246,702,286],{"class":278},[246,704,289],{"class":256},[246,706,292],{"class":278},[246,708,296],{"class":295},[246,710,711],{"class":278},",\n",[246,713,714,717,719,721,723,725],{"class":248,"line":260},[246,715,716],{"class":282},"                   det_boxes",[246,718,286],{"class":278},[246,720,289],{"class":256},[246,722,292],{"class":278},[246,724,296],{"class":295},[246,726,711],{"class":278},[246,728,729,732,734,738,741,744,746,748,750,752,754],{"class":248,"line":267},[246,730,731],{"class":282},"                   gate",[246,733,286],{"class":278},[246,735,737],{"class":736},"sZMiF"," float",[246,739,740],{"class":393}," =",[246,742,743],{"class":421}," 0.5",[246,745,313],{"class":278},[246,747,316],{"class":278},[246,749,289],{"class":256},[246,751,292],{"class":278},[246,753,296],{"class":295},[246,755,325],{"class":278},[246,757,758,761,764],{"class":248,"line":328},[246,759,760],{"class":331},"    \"\"\"",[246,762,763],{"class":338},"(1 - IoU) cost matrix with centroid-distance gating.",[246,765,766],{"class":331},"\"\"\"\n",[246,768,769,772,774,777,779,781,783,785,787,790],{"class":248,"line":335},[246,770,771],{"class":256},"    cost ",[246,773,394],{"class":393},[246,775,776],{"class":421}," 1.0",[246,778,559],{"class":393},[246,780,275],{"class":401},[246,782,279],{"class":278},[246,784,700],{"class":401},[246,786,299],{"class":278},[246,788,789],{"class":401}," det_boxes",[246,791,537],{"class":278},[246,793,794,797,799,801,803,805,807,809,811,813,816,819,822,824,826,828,830,833,836],{"class":248,"line":342},[246,795,796],{"class":256},"    centers_pred ",[246,798,394],{"class":393},[246,800,502],{"class":278},[246,802,700],{"class":256},[246,804,409],{"class":278},[246,806,413],{"class":412},[246,808,299],{"class":278},[246,810,418],{"class":278},[246,812,422],{"class":421},[246,814,815],{"class":278},"]",[246,817,818],{"class":393}," +",[246,820,821],{"class":256}," pred_boxes",[246,823,409],{"class":278},[246,825,413],{"class":412},[246,827,299],{"class":278},[246,829,473],{"class":421},[246,831,832],{"class":278},":])",[246,834,835],{"class":393}," \u002F",[246,837,838],{"class":421}," 2\n",[246,840,841,844,846,848,851,853,855,857,859,861,863,865,868,870,872,874,876,878,880,882,885],{"class":248,"line":347},[246,842,843],{"class":256},"    centers_det  ",[246,845,394],{"class":393},[246,847,502],{"class":278},[246,849,850],{"class":256},"det_boxes",[246,852,430],{"class":278},[246,854,433],{"class":412},[246,856,299],{"class":278},[246,858,438],{"class":278},[246,860,418],{"class":278},[246,862,422],{"class":421},[246,864,815],{"class":278},[246,866,867],{"class":393},"  +",[246,869,789],{"class":256},[246,871,430],{"class":278},[246,873,433],{"class":412},[246,875,299],{"class":278},[246,877,438],{"class":278},[246,879,473],{"class":421},[246,881,832],{"class":278},[246,883,884],{"class":393},"  \u002F",[246,886,838],{"class":421},[246,888,889,892,894,896,899,901,904,906,909,911,915,918,920],{"class":248,"line":353},[246,890,891],{"class":256},"    dist ",[246,893,394],{"class":393},[246,895,502],{"class":278},[246,897,898],{"class":256},"centers_pred ",[246,900,508],{"class":393},[246,902,903],{"class":256}," centers_det",[246,905,514],{"class":278},[246,907,908],{"class":401},"norm",[246,910,279],{"class":278},[246,912,914],{"class":913},"s99_P","dim",[246,916,917],{"class":393},"=-",[246,919,534],{"class":421},[246,921,537],{"class":278},[246,923,924,926,928,931,933,936,938,941,944,947,949,951,953,957,961,963],{"class":248,"line":359},[246,925,771],{"class":256},[246,927,394],{"class":393},[246,929,930],{"class":256}," cost",[246,932,292],{"class":278},[246,934,935],{"class":401},"masked_fill",[246,937,279],{"class":278},[246,939,940],{"class":401},"dist ",[246,942,943],{"class":393},">",[246,945,946],{"class":401}," gate",[246,948,299],{"class":278},[246,950,737],{"class":736},[246,952,279],{"class":278},[246,954,956],{"class":955},"sjJ54","\"",[246,958,960],{"class":959},"s_sjI","inf",[246,962,956],{"class":955},[246,964,965],{"class":278},"))\n",[246,967,968,970],{"class":248,"line":365},[246,969,642],{"class":252},[246,971,972],{"class":256}," cost\n",[187,974,975,976,978,979,981,982,292],{},"The solver treats ",[194,977,683],{}," as a forbidden edge. Rows where every entry is ",[194,980,683],{}," (because every detection is too far away) will be returned as unmatched with index ",[194,983,984],{},"-1",[182,986,988],{"id":987},"the-tracking-loop","The tracking loop",[187,990,991],{},"A minimal SORT-style tracker maintains a list of active tracks. Each track holds a predicted bounding box (updated by a Kalman filter or, for simplicity here, just carried forward) and a unique integer ID. At each frame:",[993,994,995,999,1002,1005],"ol",{},[996,997,998],"li",{},"Predict updated box positions for all existing tracks.",[996,1000,1001],{},"Build the cost matrix between predicted track boxes and incoming detections.",[996,1003,1004],{},"Solve the assignment problem.",[996,1006,1007],{},"For matched pairs: update the track with the new detection. For unmatched tracks: mark as lost. For unmatched detections: start a new track.",[219,1009,1013],{"className":1010,"code":1011,"language":1012,"meta":227,"style":227},"language-mermaid shiki shiki-themes material-theme-lighter github-light github-dark","flowchart LR\n    A[Predict\\nnext position] --> B[Build\\ncost matrix]\n    B --> C[Solve\\nassignment]\n    C --> D{Row matched?}\n    D -->|yes| E[Update track\\nwith detection]\n    D -->|no| F[Mark track\\nas lost]\n    C --> G{Column unmatched?}\n    G -->|yes| H[Initialize\\nnew track]\n","mermaid",[194,1014,1015,1020,1025,1030,1035,1040,1045,1050],{"__ignoreMap":227},[246,1016,1017],{"class":248,"line":249},[246,1018,1019],{"class":256},"flowchart LR\n",[246,1021,1022],{"class":248,"line":260},[246,1023,1024],{"class":256},"    A[Predict\\nnext position] --> B[Build\\ncost matrix]\n",[246,1026,1027],{"class":248,"line":267},[246,1028,1029],{"class":256},"    B --> C[Solve\\nassignment]\n",[246,1031,1032],{"class":248,"line":328},[246,1033,1034],{"class":256},"    C --> D{Row matched?}\n",[246,1036,1037],{"class":248,"line":335},[246,1038,1039],{"class":256},"    D -->|yes| E[Update track\\nwith detection]\n",[246,1041,1042],{"class":248,"line":342},[246,1043,1044],{"class":256},"    D -->|no| F[Mark track\\nas lost]\n",[246,1046,1047],{"class":248,"line":347},[246,1048,1049],{"class":256},"    C --> G{Column unmatched?}\n",[246,1051,1052],{"class":248,"line":353},[246,1053,1054],{"class":256},"    G -->|yes| H[Initialize\\nnew track]\n",[187,1056,1057],{},"Here is a complete, runnable implementation:",[219,1059,1061],{"className":240,"code":1060,"language":242,"meta":227,"style":227},"import torch\nimport torchmatch\nfrom dataclasses import dataclass, field\nfrom typing import Optional\n\n@dataclass\nclass Track:\n    track_id: int\n    box: torch.Tensor        # (4,) in x1, y1, x2, y2\n    lost_frames: int = 0\n\nclass SimpleTracker:\n    def __init__(self, iou_threshold: float = 0.3, max_lost: int = 3):\n        self.iou_threshold = iou_threshold\n        self.max_lost = max_lost\n        self.tracks: list[Track] = []\n        self._next_id = 0\n\n    def _new_id(self) -> int:\n        i = self._next_id\n        self._next_id += 1\n        return i\n\n    def update(self, detections: torch.Tensor) -> list[Track]:\n        \"\"\"\n        Args:\n            detections: (D, 4) bounding boxes for the current frame,\n                        in x1, y1, x2, y2 format.\n\n        Returns:\n            Active tracks after this update.\n        \"\"\"\n        if not self.tracks:\n            # No existing tracks: every detection starts a new track.\n            for box in detections:\n                self.tracks.append(Track(self._new_id(), box.clone()))\n            return self.tracks\n\n        pred_boxes = torch.stack([t.box for t in self.tracks])   # (T, 4)\n\n        if detections.numel() == 0:\n            # No detections this frame: all tracks become lost.\n            for t in self.tracks:\n                t.lost_frames += 1\n            self.tracks = [t for t in self.tracks if t.lost_frames \u003C= self.max_lost]\n            return self.tracks\n\n        # Build the gated IoU cost matrix.\n        cost = iou_cost_gated(pred_boxes, detections,\n                              gate=self.iou_threshold * 2)\n        # Forbid assignments with low IoU (high cost) outright.\n        cost = cost.masked_fill(cost > (1.0 - self.iou_threshold), float(\"inf\"))\n\n        # Solve: row_to_col[i] is the detection assigned to track i, or -1.\n        row_to_col = torchmatch.assignment.solve(cost)\n\n        matched_det_indices = set()\n        for i, t in enumerate(self.tracks):\n            j = int(row_to_col[i].item())\n            if j >= 0:\n                # Matched: update the track with the new detection box.\n                t.box = detections[j].clone()\n                t.lost_frames = 0\n                matched_det_indices.add(j)\n            else:\n                # Unmatched track: increment lost counter.\n                t.lost_frames += 1\n\n        # Remove tracks that have been lost for too long.\n        self.tracks = [t for t in self.tracks if t.lost_frames \u003C= self.max_lost]\n\n        # Unmatched detections: start a new track for each.\n        for j in range(len(detections)):\n            if j not in matched_det_indices:\n                self.tracks.append(Track(self._new_id(), detections[j].clone()))\n\n        return self.tracks\n",[194,1062,1063,1069,1076,1094,1106,1110,1119,1130,1140,1157,1172,1176,1185,1231,1247,1261,1287,1300,1304,1323,1339,1354,1363,1368,1406,1412,1418,1424,1430,1435,1441,1447,1452,1469,1475,1491,1533,1546,1551,1597,1602,1625,1631,1648,1663,1715,1726,1731,1737,1757,1778,1784,1832,1837,1843,1871,1876,1890,1918,1946,1962,1968,1991,2004,2021,2029,2035,2048,2053,2059,2104,2109,2115,2140,2158,2197,2202],{"__ignoreMap":227},[246,1064,1065,1067],{"class":248,"line":249},[246,1066,253],{"class":252},[246,1068,257],{"class":256},[246,1070,1071,1073],{"class":248,"line":260},[246,1072,253],{"class":252},[246,1074,1075],{"class":256}," torchmatch\n",[246,1077,1078,1081,1084,1086,1089,1091],{"class":248,"line":267},[246,1079,1080],{"class":252},"from",[246,1082,1083],{"class":256}," dataclasses ",[246,1085,253],{"class":252},[246,1087,1088],{"class":256}," dataclass",[246,1090,299],{"class":278},[246,1092,1093],{"class":256}," field\n",[246,1095,1096,1098,1101,1103],{"class":248,"line":328},[246,1097,1080],{"class":252},[246,1099,1100],{"class":256}," typing ",[246,1102,253],{"class":252},[246,1104,1105],{"class":256}," Optional\n",[246,1107,1108],{"class":248,"line":335},[246,1109,264],{"emptyLinePlaceholder":263},[246,1111,1112,1116],{"class":248,"line":342},[246,1113,1115],{"class":1114},"stp6e","@",[246,1117,1118],{"class":274},"dataclass\n",[246,1120,1121,1124,1128],{"class":248,"line":347},[246,1122,1123],{"class":270},"class",[246,1125,1127],{"class":1126},"sbgvK"," Track",[246,1129,325],{"class":278},[246,1131,1132,1135,1137],{"class":248,"line":353},[246,1133,1134],{"class":256},"    track_id",[246,1136,286],{"class":278},[246,1138,1139],{"class":736}," int\n",[246,1141,1142,1145,1147,1149,1151,1153],{"class":248,"line":359},[246,1143,1144],{"class":256},"    box",[246,1146,286],{"class":278},[246,1148,289],{"class":256},[246,1150,292],{"class":278},[246,1152,296],{"class":295},[246,1154,1156],{"class":1155},"sutJx","        # (4,) in x1, y1, x2, y2\n",[246,1158,1159,1162,1164,1167,1169],{"class":248,"line":365},[246,1160,1161],{"class":256},"    lost_frames",[246,1163,286],{"class":278},[246,1165,1166],{"class":736}," int",[246,1168,740],{"class":393},[246,1170,1171],{"class":421}," 0\n",[246,1173,1174],{"class":248,"line":370},[246,1175,264],{"emptyLinePlaceholder":263},[246,1177,1178,1180,1183],{"class":248,"line":376},[246,1179,1123],{"class":270},[246,1181,1182],{"class":1126}," SimpleTracker",[246,1184,325],{"class":278},[246,1186,1187,1190,1194,1196,1200,1202,1205,1207,1209,1211,1214,1216,1219,1221,1223,1225,1228],{"class":248,"line":382},[246,1188,1189],{"class":270},"    def",[246,1191,1193],{"class":1192},"sptTA"," __init__",[246,1195,279],{"class":278},[246,1197,1199],{"class":1198},"smCYv","self",[246,1201,299],{"class":278},[246,1203,1204],{"class":282}," iou_threshold",[246,1206,286],{"class":278},[246,1208,737],{"class":736},[246,1210,740],{"class":393},[246,1212,1213],{"class":421}," 0.3",[246,1215,299],{"class":278},[246,1217,1218],{"class":282}," max_lost",[246,1220,286],{"class":278},[246,1222,1166],{"class":736},[246,1224,740],{"class":393},[246,1226,1227],{"class":421}," 3",[246,1229,1230],{"class":278},"):\n",[246,1232,1233,1237,1239,1242,1244],{"class":248,"line":387},[246,1234,1236],{"class":1235},"s_hVV","        self",[246,1238,292],{"class":278},[246,1240,1241],{"class":295},"iou_threshold",[246,1243,740],{"class":393},[246,1245,1246],{"class":256}," iou_threshold\n",[246,1248,1249,1251,1253,1256,1258],{"class":248,"line":448},[246,1250,1236],{"class":1235},[246,1252,292],{"class":278},[246,1254,1255],{"class":295},"max_lost",[246,1257,740],{"class":393},[246,1259,1260],{"class":256}," max_lost\n",[246,1262,1263,1265,1267,1270,1272,1275,1277,1280,1282,1284],{"class":248,"line":494},[246,1264,1236],{"class":1235},[246,1266,292],{"class":278},[246,1268,1269],{"class":295},"tracks",[246,1271,286],{"class":278},[246,1273,1274],{"class":256}," list",[246,1276,430],{"class":278},[246,1278,1279],{"class":256},"Track",[246,1281,815],{"class":278},[246,1283,740],{"class":393},[246,1285,1286],{"class":278}," []\n",[246,1288,1289,1291,1293,1296,1298],{"class":248,"line":540},[246,1290,1236],{"class":1235},[246,1292,292],{"class":278},[246,1294,1295],{"class":295},"_next_id",[246,1297,740],{"class":393},[246,1299,1171],{"class":421},[246,1301,1302],{"class":248,"line":590},[246,1303,264],{"emptyLinePlaceholder":263},[246,1305,1306,1308,1311,1313,1315,1317,1319,1321],{"class":248,"line":639},[246,1307,1189],{"class":270},[246,1309,1310],{"class":274}," _new_id",[246,1312,279],{"class":278},[246,1314,1199],{"class":1198},[246,1316,313],{"class":278},[246,1318,316],{"class":278},[246,1320,1166],{"class":736},[246,1322,325],{"class":278},[246,1324,1326,1329,1331,1334,1336],{"class":248,"line":1325},20,[246,1327,1328],{"class":256},"        i ",[246,1330,394],{"class":393},[246,1332,1333],{"class":1235}," self",[246,1335,292],{"class":278},[246,1337,1338],{"class":295},"_next_id\n",[246,1340,1342,1344,1346,1348,1351],{"class":248,"line":1341},21,[246,1343,1236],{"class":1235},[246,1345,292],{"class":278},[246,1347,1295],{"class":295},[246,1349,1350],{"class":393}," +=",[246,1352,1353],{"class":421}," 1\n",[246,1355,1357,1360],{"class":248,"line":1356},22,[246,1358,1359],{"class":252},"        return",[246,1361,1362],{"class":256}," i\n",[246,1364,1366],{"class":248,"line":1365},23,[246,1367,264],{"emptyLinePlaceholder":263},[246,1369,1371,1373,1376,1378,1380,1382,1385,1387,1389,1391,1393,1395,1397,1399,1401,1403],{"class":248,"line":1370},24,[246,1372,1189],{"class":270},[246,1374,1375],{"class":274}," update",[246,1377,279],{"class":278},[246,1379,1199],{"class":1198},[246,1381,299],{"class":278},[246,1383,1384],{"class":282}," detections",[246,1386,286],{"class":278},[246,1388,289],{"class":256},[246,1390,292],{"class":278},[246,1392,296],{"class":295},[246,1394,313],{"class":278},[246,1396,316],{"class":278},[246,1398,1274],{"class":256},[246,1400,430],{"class":278},[246,1402,1279],{"class":256},[246,1404,1405],{"class":278},"]:\n",[246,1407,1409],{"class":248,"line":1408},25,[246,1410,1411],{"class":331},"        \"\"\"\n",[246,1413,1415],{"class":248,"line":1414},26,[246,1416,1417],{"class":338},"        Args:\n",[246,1419,1421],{"class":248,"line":1420},27,[246,1422,1423],{"class":338},"            detections: (D, 4) bounding boxes for the current frame,\n",[246,1425,1427],{"class":248,"line":1426},28,[246,1428,1429],{"class":338},"                        in x1, y1, x2, y2 format.\n",[246,1431,1433],{"class":248,"line":1432},29,[246,1434,264],{"emptyLinePlaceholder":263},[246,1436,1438],{"class":248,"line":1437},30,[246,1439,1440],{"class":338},"        Returns:\n",[246,1442,1444],{"class":248,"line":1443},31,[246,1445,1446],{"class":338},"            Active tracks after this update.\n",[246,1448,1450],{"class":248,"line":1449},32,[246,1451,1411],{"class":331},[246,1453,1455,1458,1461,1463,1465,1467],{"class":248,"line":1454},33,[246,1456,1457],{"class":252},"        if",[246,1459,1460],{"class":393}," not",[246,1462,1333],{"class":1235},[246,1464,292],{"class":278},[246,1466,1269],{"class":295},[246,1468,325],{"class":278},[246,1470,1472],{"class":248,"line":1471},34,[246,1473,1474],{"class":1155},"            # No existing tracks: every detection starts a new track.\n",[246,1476,1478,1481,1484,1487,1489],{"class":248,"line":1477},35,[246,1479,1480],{"class":252},"            for",[246,1482,1483],{"class":256}," box ",[246,1485,1486],{"class":252},"in",[246,1488,1384],{"class":256},[246,1490,325],{"class":278},[246,1492,1494,1497,1499,1501,1503,1506,1508,1510,1512,1514,1516,1519,1522,1525,1527,1530],{"class":248,"line":1493},36,[246,1495,1496],{"class":1235},"                self",[246,1498,292],{"class":278},[246,1500,1269],{"class":295},[246,1502,292],{"class":278},[246,1504,1505],{"class":401},"append",[246,1507,279],{"class":278},[246,1509,1279],{"class":401},[246,1511,279],{"class":278},[246,1513,1199],{"class":1235},[246,1515,292],{"class":278},[246,1517,1518],{"class":401},"_new_id",[246,1520,1521],{"class":278},"(),",[246,1523,1524],{"class":401}," box",[246,1526,292],{"class":278},[246,1528,1529],{"class":401},"clone",[246,1531,1532],{"class":278},"()))\n",[246,1534,1536,1539,1541,1543],{"class":248,"line":1535},37,[246,1537,1538],{"class":252},"            return",[246,1540,1333],{"class":1235},[246,1542,292],{"class":278},[246,1544,1545],{"class":295},"tracks\n",[246,1547,1549],{"class":248,"line":1548},38,[246,1550,264],{"emptyLinePlaceholder":263},[246,1552,1554,1557,1559,1561,1563,1566,1569,1572,1574,1577,1580,1583,1585,1587,1589,1591,1594],{"class":248,"line":1553},39,[246,1555,1556],{"class":256},"        pred_boxes ",[246,1558,394],{"class":393},[246,1560,289],{"class":256},[246,1562,292],{"class":278},[246,1564,1565],{"class":401},"stack",[246,1567,1568],{"class":278},"([",[246,1570,1571],{"class":401},"t",[246,1573,292],{"class":278},[246,1575,1576],{"class":295},"box",[246,1578,1579],{"class":252}," for",[246,1581,1582],{"class":401}," t ",[246,1584,1486],{"class":252},[246,1586,1333],{"class":1235},[246,1588,292],{"class":278},[246,1590,1269],{"class":295},[246,1592,1593],{"class":278},"])",[246,1595,1596],{"class":1155},"   # (T, 4)\n",[246,1598,1600],{"class":248,"line":1599},40,[246,1601,264],{"emptyLinePlaceholder":263},[246,1603,1605,1607,1609,1611,1614,1617,1620,1623],{"class":248,"line":1604},41,[246,1606,1457],{"class":252},[246,1608,1384],{"class":256},[246,1610,292],{"class":278},[246,1612,1613],{"class":401},"numel",[246,1615,1616],{"class":278},"()",[246,1618,1619],{"class":393}," ==",[246,1621,1622],{"class":421}," 0",[246,1624,325],{"class":278},[246,1626,1628],{"class":248,"line":1627},42,[246,1629,1630],{"class":1155},"            # No detections this frame: all tracks become lost.\n",[246,1632,1634,1636,1638,1640,1642,1644,1646],{"class":248,"line":1633},43,[246,1635,1480],{"class":252},[246,1637,1582],{"class":256},[246,1639,1486],{"class":252},[246,1641,1333],{"class":1235},[246,1643,292],{"class":278},[246,1645,1269],{"class":295},[246,1647,325],{"class":278},[246,1649,1651,1654,1656,1659,1661],{"class":248,"line":1650},44,[246,1652,1653],{"class":256},"                t",[246,1655,292],{"class":278},[246,1657,1658],{"class":295},"lost_frames",[246,1660,1350],{"class":393},[246,1662,1353],{"class":421},[246,1664,1666,1669,1671,1673,1675,1678,1681,1684,1686,1688,1690,1692,1694,1697,1700,1702,1704,1707,1709,1711,1713],{"class":248,"line":1665},45,[246,1667,1668],{"class":1235},"            self",[246,1670,292],{"class":278},[246,1672,1269],{"class":295},[246,1674,740],{"class":393},[246,1676,1677],{"class":278}," [",[246,1679,1680],{"class":256},"t ",[246,1682,1683],{"class":252},"for",[246,1685,1582],{"class":256},[246,1687,1486],{"class":252},[246,1689,1333],{"class":1235},[246,1691,292],{"class":278},[246,1693,1269],{"class":295},[246,1695,1696],{"class":252}," if",[246,1698,1699],{"class":256}," t",[246,1701,292],{"class":278},[246,1703,1658],{"class":295},[246,1705,1706],{"class":393}," \u003C=",[246,1708,1333],{"class":1235},[246,1710,292],{"class":278},[246,1712,1255],{"class":295},[246,1714,587],{"class":278},[246,1716,1718,1720,1722,1724],{"class":248,"line":1717},46,[246,1719,1538],{"class":252},[246,1721,1333],{"class":1235},[246,1723,292],{"class":278},[246,1725,1545],{"class":295},[246,1727,1729],{"class":248,"line":1728},47,[246,1730,264],{"emptyLinePlaceholder":263},[246,1732,1734],{"class":248,"line":1733},48,[246,1735,1736],{"class":1155},"        # Build the gated IoU cost matrix.\n",[246,1738,1740,1743,1745,1747,1749,1751,1753,1755],{"class":248,"line":1739},49,[246,1741,1742],{"class":256},"        cost ",[246,1744,394],{"class":393},[246,1746,695],{"class":401},[246,1748,279],{"class":278},[246,1750,700],{"class":401},[246,1752,299],{"class":278},[246,1754,1384],{"class":401},[246,1756,711],{"class":278},[246,1758,1760,1763,1765,1767,1769,1771,1774,1776],{"class":248,"line":1759},50,[246,1761,1762],{"class":913},"                              gate",[246,1764,394],{"class":393},[246,1766,1199],{"class":1235},[246,1768,292],{"class":278},[246,1770,1241],{"class":295},[246,1772,1773],{"class":393}," *",[246,1775,473],{"class":421},[246,1777,537],{"class":278},[246,1779,1781],{"class":248,"line":1780},51,[246,1782,1783],{"class":1155},"        # Forbid assignments with low IoU (high cost) outright.\n",[246,1785,1787,1789,1791,1793,1795,1797,1799,1802,1804,1806,1809,1811,1813,1815,1817,1820,1822,1824,1826,1828,1830],{"class":248,"line":1786},52,[246,1788,1742],{"class":256},[246,1790,394],{"class":393},[246,1792,930],{"class":256},[246,1794,292],{"class":278},[246,1796,935],{"class":401},[246,1798,279],{"class":278},[246,1800,1801],{"class":401},"cost ",[246,1803,943],{"class":393},[246,1805,502],{"class":278},[246,1807,1808],{"class":421},"1.0",[246,1810,559],{"class":393},[246,1812,1333],{"class":1235},[246,1814,292],{"class":278},[246,1816,1241],{"class":295},[246,1818,1819],{"class":278},"),",[246,1821,737],{"class":736},[246,1823,279],{"class":278},[246,1825,956],{"class":955},[246,1827,960],{"class":959},[246,1829,956],{"class":955},[246,1831,965],{"class":278},[246,1833,1835],{"class":248,"line":1834},53,[246,1836,264],{"emptyLinePlaceholder":263},[246,1838,1840],{"class":248,"line":1839},54,[246,1841,1842],{"class":1155},"        # Solve: row_to_col[i] is the detection assigned to track i, or -1.\n",[246,1844,1846,1849,1851,1854,1856,1859,1861,1864,1866,1869],{"class":248,"line":1845},55,[246,1847,1848],{"class":256},"        row_to_col ",[246,1850,394],{"class":393},[246,1852,1853],{"class":256}," torchmatch",[246,1855,292],{"class":278},[246,1857,1858],{"class":295},"assignment",[246,1860,292],{"class":278},[246,1862,1863],{"class":401},"solve",[246,1865,279],{"class":278},[246,1867,1868],{"class":401},"cost",[246,1870,537],{"class":278},[246,1872,1874],{"class":248,"line":1873},56,[246,1875,264],{"emptyLinePlaceholder":263},[246,1877,1879,1882,1884,1887],{"class":248,"line":1878},57,[246,1880,1881],{"class":256},"        matched_det_indices ",[246,1883,394],{"class":393},[246,1885,1886],{"class":736}," set",[246,1888,1889],{"class":278},"()\n",[246,1891,1893,1896,1899,1901,1903,1905,1908,1910,1912,1914,1916],{"class":248,"line":1892},58,[246,1894,1895],{"class":252},"        for",[246,1897,1898],{"class":256}," i",[246,1900,299],{"class":278},[246,1902,1582],{"class":256},[246,1904,1486],{"class":252},[246,1906,1907],{"class":1192}," enumerate",[246,1909,279],{"class":278},[246,1911,1199],{"class":1235},[246,1913,292],{"class":278},[246,1915,1269],{"class":295},[246,1917,1230],{"class":278},[246,1919,1921,1924,1926,1928,1930,1933,1935,1937,1940,1943],{"class":248,"line":1920},59,[246,1922,1923],{"class":256},"            j ",[246,1925,394],{"class":393},[246,1927,1166],{"class":736},[246,1929,279],{"class":278},[246,1931,1932],{"class":401},"row_to_col",[246,1934,430],{"class":278},[246,1936,200],{"class":401},[246,1938,1939],{"class":278},"].",[246,1941,1942],{"class":401},"item",[246,1944,1945],{"class":278},"())\n",[246,1947,1949,1952,1955,1958,1960],{"class":248,"line":1948},60,[246,1950,1951],{"class":252},"            if",[246,1953,1954],{"class":256}," j ",[246,1956,1957],{"class":393},">=",[246,1959,1622],{"class":421},[246,1961,325],{"class":278},[246,1963,1965],{"class":248,"line":1964},61,[246,1966,1967],{"class":1155},"                # Matched: update the track with the new detection box.\n",[246,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989],{"class":248,"line":1970},62,[246,1972,1653],{"class":256},[246,1974,292],{"class":278},[246,1976,1576],{"class":295},[246,1978,740],{"class":393},[246,1980,1384],{"class":256},[246,1982,430],{"class":278},[246,1984,204],{"class":256},[246,1986,1939],{"class":278},[246,1988,1529],{"class":401},[246,1990,1889],{"class":278},[246,1992,1994,1996,1998,2000,2002],{"class":248,"line":1993},63,[246,1995,1653],{"class":256},[246,1997,292],{"class":278},[246,1999,1658],{"class":295},[246,2001,740],{"class":393},[246,2003,1171],{"class":421},[246,2005,2007,2010,2012,2015,2017,2019],{"class":248,"line":2006},64,[246,2008,2009],{"class":256},"                matched_det_indices",[246,2011,292],{"class":278},[246,2013,2014],{"class":401},"add",[246,2016,279],{"class":278},[246,2018,204],{"class":401},[246,2020,537],{"class":278},[246,2022,2024,2027],{"class":248,"line":2023},65,[246,2025,2026],{"class":252},"            else",[246,2028,325],{"class":278},[246,2030,2032],{"class":248,"line":2031},66,[246,2033,2034],{"class":1155},"                # Unmatched track: increment lost counter.\n",[246,2036,2038,2040,2042,2044,2046],{"class":248,"line":2037},67,[246,2039,1653],{"class":256},[246,2041,292],{"class":278},[246,2043,1658],{"class":295},[246,2045,1350],{"class":393},[246,2047,1353],{"class":421},[246,2049,2051],{"class":248,"line":2050},68,[246,2052,264],{"emptyLinePlaceholder":263},[246,2054,2056],{"class":248,"line":2055},69,[246,2057,2058],{"class":1155},"        # Remove tracks that have been lost for too long.\n",[246,2060,2062,2064,2066,2068,2070,2072,2074,2076,2078,2080,2082,2084,2086,2088,2090,2092,2094,2096,2098,2100,2102],{"class":248,"line":2061},70,[246,2063,1236],{"class":1235},[246,2065,292],{"class":278},[246,2067,1269],{"class":295},[246,2069,740],{"class":393},[246,2071,1677],{"class":278},[246,2073,1680],{"class":256},[246,2075,1683],{"class":252},[246,2077,1582],{"class":256},[246,2079,1486],{"class":252},[246,2081,1333],{"class":1235},[246,2083,292],{"class":278},[246,2085,1269],{"class":295},[246,2087,1696],{"class":252},[246,2089,1699],{"class":256},[246,2091,292],{"class":278},[246,2093,1658],{"class":295},[246,2095,1706],{"class":393},[246,2097,1333],{"class":1235},[246,2099,292],{"class":278},[246,2101,1255],{"class":295},[246,2103,587],{"class":278},[246,2105,2107],{"class":248,"line":2106},71,[246,2108,264],{"emptyLinePlaceholder":263},[246,2110,2112],{"class":248,"line":2111},72,[246,2113,2114],{"class":1155},"        # Unmatched detections: start a new track for each.\n",[246,2116,2118,2120,2122,2124,2127,2129,2132,2134,2137],{"class":248,"line":2117},73,[246,2119,1895],{"class":252},[246,2121,1954],{"class":256},[246,2123,1486],{"class":252},[246,2125,2126],{"class":1192}," range",[246,2128,279],{"class":278},[246,2130,2131],{"class":1192},"len",[246,2133,279],{"class":278},[246,2135,2136],{"class":401},"detections",[246,2138,2139],{"class":278},")):\n",[246,2141,2143,2145,2147,2150,2153,2156],{"class":248,"line":2142},74,[246,2144,1951],{"class":252},[246,2146,1954],{"class":256},[246,2148,2149],{"class":393},"not",[246,2151,2152],{"class":393}," in",[246,2154,2155],{"class":256}," matched_det_indices",[246,2157,325],{"class":278},[246,2159,2161,2163,2165,2167,2169,2171,2173,2175,2177,2179,2181,2183,2185,2187,2189,2191,2193,2195],{"class":248,"line":2160},75,[246,2162,1496],{"class":1235},[246,2164,292],{"class":278},[246,2166,1269],{"class":295},[246,2168,292],{"class":278},[246,2170,1505],{"class":401},[246,2172,279],{"class":278},[246,2174,1279],{"class":401},[246,2176,279],{"class":278},[246,2178,1199],{"class":1235},[246,2180,292],{"class":278},[246,2182,1518],{"class":401},[246,2184,1521],{"class":278},[246,2186,1384],{"class":401},[246,2188,430],{"class":278},[246,2190,204],{"class":401},[246,2192,1939],{"class":278},[246,2194,1529],{"class":401},[246,2196,1532],{"class":278},[246,2198,2200],{"class":248,"line":2199},76,[246,2201,264],{"emptyLinePlaceholder":263},[246,2203,2205,2207,2209,2211],{"class":248,"line":2204},77,[246,2206,1359],{"class":252},[246,2208,1333],{"class":1235},[246,2210,292],{"class":278},[246,2212,1545],{"class":295},[182,2214,2216],{"id":2215},"reading-the-output","Reading the output",[187,2218,2219,2220,2223,2224,2226],{},"After each call to ",[194,2221,2222],{},"update",", the return value is the list of active tracks. Each track carries its integer ID (stable across frames while matched) and its current bounding box. A track absent from the active list has been dropped (lost for more than ",[194,2225,1255],{}," consecutive frames).",[187,2228,2229],{},"To see matched pairs, unmatched tracks (lost), and unmatched detections (new objects) explicitly:",[219,2231,2233],{"className":240,"code":2232,"language":242,"meta":227,"style":227},"# After the solve step:\nmatched_tracks  = [t for i, t in enumerate(tracker.tracks)\n                   if int(row_to_col[i].item()) >= 0]\nlost_tracks     = [t for i, t in enumerate(tracker.tracks)\n                   if int(row_to_col[i].item()) \u003C 0]\nnew_detections  = [j for j in range(len(detections))\n                   if j not in matched_det_indices]\n",[194,2234,2235,2240,2274,2303,2336,2363,2393],{"__ignoreMap":227},[246,2236,2237],{"class":248,"line":249},[246,2238,2239],{"class":1155},"# After the solve step:\n",[246,2241,2242,2245,2247,2249,2251,2253,2255,2257,2259,2261,2263,2265,2268,2270,2272],{"class":248,"line":260},[246,2243,2244],{"class":256},"matched_tracks  ",[246,2246,394],{"class":393},[246,2248,1677],{"class":278},[246,2250,1680],{"class":256},[246,2252,1683],{"class":252},[246,2254,1898],{"class":256},[246,2256,299],{"class":278},[246,2258,1582],{"class":256},[246,2260,1486],{"class":252},[246,2262,1907],{"class":1192},[246,2264,279],{"class":278},[246,2266,2267],{"class":401},"tracker",[246,2269,292],{"class":278},[246,2271,1269],{"class":295},[246,2273,537],{"class":278},[246,2275,2276,2279,2281,2283,2285,2287,2289,2291,2293,2296,2299,2301],{"class":248,"line":267},[246,2277,2278],{"class":252},"                   if",[246,2280,1166],{"class":736},[246,2282,279],{"class":278},[246,2284,1932],{"class":401},[246,2286,430],{"class":278},[246,2288,200],{"class":401},[246,2290,1939],{"class":278},[246,2292,1942],{"class":401},[246,2294,2295],{"class":278},"())",[246,2297,2298],{"class":393}," >=",[246,2300,1622],{"class":421},[246,2302,587],{"class":278},[246,2304,2305,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326,2328,2330,2332,2334],{"class":248,"line":328},[246,2306,2307],{"class":256},"lost_tracks     ",[246,2309,394],{"class":393},[246,2311,1677],{"class":278},[246,2313,1680],{"class":256},[246,2315,1683],{"class":252},[246,2317,1898],{"class":256},[246,2319,299],{"class":278},[246,2321,1582],{"class":256},[246,2323,1486],{"class":252},[246,2325,1907],{"class":1192},[246,2327,279],{"class":278},[246,2329,2267],{"class":401},[246,2331,292],{"class":278},[246,2333,1269],{"class":295},[246,2335,537],{"class":278},[246,2337,2338,2340,2342,2344,2346,2348,2350,2352,2354,2356,2359,2361],{"class":248,"line":335},[246,2339,2278],{"class":252},[246,2341,1166],{"class":736},[246,2343,279],{"class":278},[246,2345,1932],{"class":401},[246,2347,430],{"class":278},[246,2349,200],{"class":401},[246,2351,1939],{"class":278},[246,2353,1942],{"class":401},[246,2355,2295],{"class":278},[246,2357,2358],{"class":393}," \u003C",[246,2360,1622],{"class":421},[246,2362,587],{"class":278},[246,2364,2365,2368,2370,2372,2375,2377,2379,2381,2383,2385,2387,2389,2391],{"class":248,"line":342},[246,2366,2367],{"class":256},"new_detections  ",[246,2369,394],{"class":393},[246,2371,1677],{"class":278},[246,2373,2374],{"class":256},"j ",[246,2376,1683],{"class":252},[246,2378,1954],{"class":256},[246,2380,1486],{"class":252},[246,2382,2126],{"class":1192},[246,2384,279],{"class":278},[246,2386,2131],{"class":1192},[246,2388,279],{"class":278},[246,2390,2136],{"class":401},[246,2392,965],{"class":278},[246,2394,2395,2397,2399,2401,2403,2405],{"class":248,"line":347},[246,2396,2278],{"class":252},[246,2398,1954],{"class":256},[246,2400,2149],{"class":393},[246,2402,2152],{"class":393},[246,2404,2155],{"class":256},[246,2406,587],{"class":278},[182,2408,2410],{"id":2409},"batching-all-frames-for-performance","Batching all frames for performance",[187,2412,2413,2414,2416,2417,2420,2421,2423],{},"The loop above calls ",[194,2415,1863],{}," once per frame. When processing a batch of frames offline (for evaluation or training), it is faster to build a batched cost tensor of shape ",[194,2418,2419],{},"(B, T, D)"," and call ",[194,2422,1863],{}," once:",[219,2425,2427],{"className":240,"code":2426,"language":242,"meta":227,"style":227},"# costs: (B, T, D) where B=frames, T=tracks, D=detections per frame\nbatch_assignments = torchmatch.assignment.solve(costs)   # (B, T)\n\n# Recover matched\u002Funmatched per frame using the unpacked variant:\nmatches, unmatched_tracks, unmatched_dets, n_matched = torchmatch.assignment.solve(\n    costs, unpack=True,\n)\n# matches[b, :n_matched[b]] are the matched (track, detection) pairs for frame b.\n",[194,2428,2429,2434,2461,2465,2470,2505,2522,2526],{"__ignoreMap":227},[246,2430,2431],{"class":248,"line":249},[246,2432,2433],{"class":1155},"# costs: (B, T, D) where B=frames, T=tracks, D=detections per frame\n",[246,2435,2436,2439,2441,2443,2445,2447,2449,2451,2453,2456,2458],{"class":248,"line":260},[246,2437,2438],{"class":256},"batch_assignments ",[246,2440,394],{"class":393},[246,2442,1853],{"class":256},[246,2444,292],{"class":278},[246,2446,1858],{"class":295},[246,2448,292],{"class":278},[246,2450,1863],{"class":401},[246,2452,279],{"class":278},[246,2454,2455],{"class":401},"costs",[246,2457,313],{"class":278},[246,2459,2460],{"class":1155},"   # (B, T)\n",[246,2462,2463],{"class":248,"line":267},[246,2464,264],{"emptyLinePlaceholder":263},[246,2466,2467],{"class":248,"line":328},[246,2468,2469],{"class":1155},"# Recover matched\u002Funmatched per frame using the unpacked variant:\n",[246,2471,2472,2475,2477,2480,2482,2485,2487,2490,2492,2494,2496,2498,2500,2502],{"class":248,"line":335},[246,2473,2474],{"class":256},"matches",[246,2476,299],{"class":278},[246,2478,2479],{"class":256}," unmatched_tracks",[246,2481,299],{"class":278},[246,2483,2484],{"class":256}," unmatched_dets",[246,2486,299],{"class":278},[246,2488,2489],{"class":256}," n_matched ",[246,2491,394],{"class":393},[246,2493,1853],{"class":256},[246,2495,292],{"class":278},[246,2497,1858],{"class":295},[246,2499,292],{"class":278},[246,2501,1863],{"class":401},[246,2503,2504],{"class":278},"(\n",[246,2506,2507,2510,2512,2515,2517,2520],{"class":248,"line":342},[246,2508,2509],{"class":401},"    costs",[246,2511,299],{"class":278},[246,2513,2514],{"class":913}," unpack",[246,2516,394],{"class":393},[246,2518,2519],{"class":412},"True",[246,2521,711],{"class":278},[246,2523,2524],{"class":248,"line":347},[246,2525,537],{"class":278},[246,2527,2528],{"class":248,"line":353},[246,2529,2530],{"class":1155},"# matches[b, :n_matched[b]] are the matched (track, detection) pairs for frame b.\n",[187,2532,2533,2534,2537,2538,2541,2542,2546],{},"The batched path distributes frames across CPU threads via ",[194,2535,2536],{},"parallel_for",". For square problems with ",[194,2539,2540],{},"T == D \u003C= 64",", the CUDA backend is also available and can be graph-captured for minimal kernel-launch overhead. See ",[2543,2544,2545],"a",{"href":52},"Backends and batching"," for the dispatch rules.",[182,2548,2550],{"id":2549},"going-further","Going further",[187,2552,2553,2554,2558,2559,2562,2563,2566],{},"The tracker above uses a constant velocity model (no motion prediction). Real-world systems like SORT ",[2555,2556],"docyard-cite",{"bib":2557},"Bewley2016"," wrap each track in a Kalman filter that predicts the next-frame box before building the cost matrix. ByteTrack ",[2555,2560],{"bib":2561},"Zhang2022"," and BoT-SORT ",[2555,2564],{"bib":2565},"Aharon2022"," extend this with appearance embeddings and camera-motion compensation, but the assignment step at the core remains the same: build a cost matrix, call a LAP solver, unpack matched and unmatched sets.",[182,2568,2570],{"id":2569},"see-also","See also",[2572,2573,2574,2579],"ul",{},[996,2575,2576,2578],{},[2543,2577,33],{"href":34},": exact op signatures and output shapes.",[996,2580,2581,2583],{},[2543,2582,156],{"href":157},": latency numbers for batched IoU cost problems across hardware.",[2585,2586],"bibliography",{},[2588,2589,2590],"style",{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sFwrP, html code.shiki .sFwrP{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s2W-s, html code.shiki .s2W-s{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .sithA, html code.shiki .sithA{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smCYv, html code.shiki .smCYv{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":227,"searchDepth":267,"depth":267,"links":2592},[2593,2594,2595,2596,2597,2598,2599,2600],{"id":184,"depth":260,"text":185},{"id":208,"depth":260,"text":209},{"id":673,"depth":260,"text":674},{"id":987,"depth":260,"text":988},{"id":2215,"depth":260,"text":2216},{"id":2409,"depth":260,"text":2410},{"id":2549,"depth":260,"text":2550},{"id":2569,"depth":260,"text":2570},"Building a SORT-style multi-object tracker step by step using torchmatch.assignment.solve and IoU cost matrices.","md",{},{"title":55},{"title":176,"description":2601},"zSSUMc7muJU8ADjbvc96Q3NgAfhE425xVa3HmgxUliI",[2608,2610],{"title":51,"path":52,"stem":53,"description":2609,"children":-1},"Choosing between jonker_scalar, jonker_dense, jonker_compact, munkres, and lawler, and how to solve thousands of problems at once.",{"title":59,"path":60,"stem":61,"description":2611,"children":-1},"Optimal transport solvers — Sinkhorn, Sinkhorn divergence, unbalanced OT, and exact EMD — registered as PyTorch custom ops.",[2613,2623,2629,2639,2649,2658,2668,2676,2685,2695,2704,2714,2723,2732,2742,2750,2760,2771,2780,2789,2796,2806,2816,2826,2834,2844,2851,2860,2869,2877,2884,2891,2900,2905,2914,2923,2931,2941,2947,2952,2957,2967,2975,2985,2992,3001],{"key":2614,"type":2615,"author":2616,"title":2617,"journal":2618,"year":2619,"volume":2620,"pages":2621,"note":2622},"Konig1931","article","Kőnig, Dénes","Gráfok és mátrixok","Matematikai és Fizikai Lapok","1931","38","116–119","Hungarian. Origin of Kőnig's theorem on bipartite matchings and covers.",{"key":2624,"type":2615,"author":2625,"title":2626,"journal":2618,"year":2619,"volume":2620,"pages":2627,"note":2628},"Egervary1931","Egerváry, Jenő","Matrixok kombinatorius tulajdonságairól","16–28","Hungarian. Weighted extension of Kőnig's theorem. Translated to English by H. W. Kuhn (1955) as “On Combinatorial Properties of Matrices”, Logistics Papers Issue 11, George Washington University.",{"key":2630,"type":2615,"author":2631,"title":2632,"journal":2633,"year":2634,"volume":422,"number":2635,"pages":2636,"doi":2637,"note":2638},"Kuhn1955","Kuhn, Harold W.","The Hungarian method for the assignment problem","Naval Research Logistics Quarterly","1955","1--2","83–97","10.1002\u002Fnav.3800020109","Coined the name “Hungarian method” in tribute to Kőnig and Egerváry; first explicit O(n^4) algorithm for the LAP.",{"key":2640,"type":2615,"author":2641,"title":2642,"journal":2643,"year":2644,"volume":2645,"number":534,"pages":2646,"doi":2647,"note":2648},"Munkres1957","Munkres, James","Algorithms for the assignment and transportation problems","Journal of the Society for Industrial and Applied Mathematics","1957","5","32–38","10.1137\u002F0105003","Six-step state machine on primed and starred zeros under row\u002Fcolumn covers; canonical textbook formulation of the Hungarian method. Implemented as torchmatch.munkres.",{"key":2650,"type":2615,"author":2651,"title":2652,"journal":2653,"year":2654,"volume":534,"number":422,"pages":2655,"doi":2656,"note":2657},"Tomizawa1971","Tomizawa, Nobuaki","On some techniques useful for solution of transportation network problems","Networks","1971","173–194","10.1002\u002Fnet.3230010206","Independent reduction of the Hungarian method to O(n^3) via shortest augmenting paths; predates Edmonds & Karp (1972).",{"key":2659,"type":2615,"author":2660,"title":2661,"journal":2662,"year":2663,"volume":2664,"number":422,"pages":2665,"doi":2666,"note":2667},"EdmondsKarp1972","Edmonds, Jack and Karp, Richard M.","Theoretical improvements in algorithmic efficiency for network flow problems","Journal of the ACM","1972","19","248–264","10.1145\u002F321694.321699","Independent O(n^3) improvement to the Hungarian method via shortest augmenting paths in network flow; contemporary with [Tomizawa1971].",{"key":2669,"type":2670,"author":2671,"title":2672,"publisher":2673,"year":2674,"note":2675},"Lawler1976","book","Lawler, Eugene L.","Combinatorial Optimization: Networks and Matroids","Holt, Rinehart and Winston","1976","Tree-augmentation reformulation of the Hungarian inner loop; basis for the GPU-parallel BFS structure used in torchmatch.lawler.",{"key":2677,"type":2678,"author":2679,"title":2680,"year":2681,"url":2682,"institution":2683,"note":2684},"Bertsekas1979","techreport","Bertsekas, Dimitri P.","A distributed algorithm for the assignment problem","1979","https:\u002F\u002Fweb.mit.edu\u002Fdimitrib\u002Fwww\u002FBertsekas_Auction_Distributed_1979.pdf","Laboratory for Information and Decision Systems, MIT","Original distributed-bidding (“auction”) algorithm for the LAP; precursor to the ε-scaling version in [Bertsekas1988].",{"key":2686,"type":2615,"author":2687,"title":2688,"journal":2689,"year":2690,"volume":2620,"number":2691,"pages":2692,"doi":2693,"note":2694},"JonkerVolgenant1987","Jonker, Roy and Volgenant, Anton","A shortest augmenting path algorithm for dense and sparse linear assignment problems","Computing","1987","4","325–340","10.1007\u002FBF02278710","Dijkstra-based SSP with column-reduction warm start and reduction transfer; basis for torchmatch.jonker\\_* on CPU.",{"key":2696,"type":2615,"author":2679,"title":2697,"journal":2698,"year":2699,"volume":2700,"number":534,"pages":2701,"doi":2702,"note":2703},"Bertsekas1988","The auction algorithm: a distributed relaxation method for the assignment problem","Annals of Operations Research","1988","14","105–123","10.1007\u002FBF02186476","Auction with ε-scaling; convergence guarantees. Widely cited follow-up to [Bertsekas1979].",{"key":2705,"type":2615,"author":2706,"title":2707,"journal":2708,"year":2709,"volume":2710,"number":422,"pages":2711,"doi":2712,"note":2713},"GoldbergKennedy1995","Goldberg, Andrew V. and Kennedy, Robert","An efficient cost scaling algorithm for the assignment problem","Mathematical Programming","1995","71","153–177","10.1007\u002FBF01585996","Push-relabel cost-scaling for the LAP via min-cost flow reduction.",{"key":2715,"type":2716,"author":2631,"title":2632,"booktitle":2717,"publisher":2718,"year":2719,"pages":2720,"doi":2721,"note":2722},"Kuhn2010Variants","incollection","50 Years of Integer Programming 1958–2008","Springer","2010","29–47","10.1007\u002F978-3-540-68279-0_2","Author's retrospective on the 1955 paper; clarifies the attribution chain to Kőnig and Egerváry.",{"key":2724,"type":2670,"author":2725,"title":2726,"publisher":2727,"year":2728,"doi":2729,"edition":2730,"note":2731},"BurkardDellAmicoMartello2012","Burkard, Rainer E. and Dell'Amico, Mauro and Martello, Silvano","Assignment Problems","Society for Industrial and Applied Mathematics","2012","10.1137\u002F1.9781611972238","Revised reprint","Comprehensive treatment of LAP variants, complexity bounds, and empirical comparisons. Original 2009; this is the 2012 revised reprint with corrections.",{"key":2733,"type":2615,"author":2734,"title":2735,"journal":2736,"year":2737,"volume":2738,"number":2691,"pages":2739,"doi":2740,"note":2741},"Crouse2016","Crouse, David F.","On implementing 2D rectangular assignment algorithms","IEEE Transactions on Aerospace and Electronic Systems","2016","52","1679–1696","10.1109\u002FTAES.2016.140952","Rectangular-native reformulation of Jonker-Volgenant; basis for torchmatch.jonker\\_dense and the tiled CUDA backend of torchmatch.jonker\\_dense\\_batch.",{"key":2743,"type":2615,"author":2744,"title":2745,"journal":2746,"year":2747,"pages":2748,"note":2749},"Monge1781","Monge, Gaspard","Mémoire sur la théorie des déblais et des remblais","Histoire de l'Académie Royale des Sciences","1781","666–704","Foundational deterministic mass-transport problem; no convex relaxation, making it hard to solve in general.",{"key":2751,"type":2615,"author":2752,"title":2753,"journal":2754,"year":2755,"volume":2756,"number":2757,"pages":2758,"note":2759},"Kantorovich1942","Kantorovich, Leonid V.","On the translocation of masses","Doklady Akademii Nauk USSR","1942","37","7--8","199–201","LP relaxation of [Monge1781]: couplings instead of bijections; strong duality yields dual variables interpretable as prices.",{"key":2761,"type":2615,"author":2762,"title":2763,"journal":2764,"year":2765,"volume":2766,"number":2767,"pages":2768,"doi":2769,"note":2770},"BenamouBrenier2000","Benamou, Jean-David and Brenier, Yann","A computational fluid mechanics solution to the Monge-Kantorovich mass transfer problem","Numerische Mathematik","2000","84","3","375–393","10.1007\u002Fs002110050263","Dynamic OT: W_2^2 equals the minimum kinetic energy to deform one distribution into the other; connects OT to PDEs.",{"key":2772,"type":2615,"author":2773,"title":2774,"journal":2775,"year":2765,"volume":2776,"number":422,"pages":2777,"doi":2778,"note":2779},"Rubner2000","Rubner, Yossi and Tomasi, Carlo and Guibas, Leonidas J.","The earth mover's distance as a metric for image retrieval","International Journal of Computer Vision","40","99–121","10.1023\u002FA:1026543900054","Introduced the Earth Mover's Distance (EMD) as a practical histogram similarity measure; established OT as a tool in computer vision.",{"key":2781,"type":2670,"author":2782,"title":2783,"publisher":2784,"year":2785,"volume":2786,"doi":2787,"note":2788},"Villani2003","Villani, Cédric","Topics in Optimal Transportation","American Mathematical Society","2003","58","10.1090\u002Fgsm\u002F058","First of Villani's two monographs on OT; introduces displacement interpolation and unifies geometry, analysis, and probability.",{"key":2790,"type":2670,"author":2782,"title":2791,"publisher":2718,"year":2792,"volume":2793,"doi":2794,"note":2795},"Villani2008","Optimal Transport: Old and New","2008","338","10.1007\u002F978-3-540-71050-9","Second of Villani's monographs; comprehensive treatment including the Brenier map, regularity, and geometric applications. Fields Medal (2010) awarded partly for this body of work.",{"key":2797,"type":2615,"author":2798,"title":2799,"journal":2800,"year":2801,"volume":2802,"number":2691,"pages":2803,"doi":2804,"note":2805},"Sinkhorn1967","Sinkhorn, Richard","Diagonal equivalence to matrices with prescribed row and column sums","The American Mathematical Monthly","1967","74","402–405","10.2307\u002F2314570","Proves convergence of the Sinkhorn–Knopp matrix-scaling iteration; foundational for all entropic OT solvers.",{"key":2807,"type":2808,"author":2809,"title":2810,"booktitle":2811,"publisher":2812,"year":2813,"volume":2814,"note":2815},"Cuturi2013","inproceedings","Cuturi, Marco","Sinkhorn distances: lightspeed computation of optimal transportation distances","Advances in Neural Information Processing Systems","Curran Associates","2013","26","Entropic regularisation of OT; reduction to Sinkhorn iteration enables large-scale batched OT on GPU. Direct ancestor of the LOG\\_SINKHORN backend in torchmatch.",{"key":2817,"type":2808,"author":2818,"title":2819,"booktitle":2820,"publisher":2821,"year":2822,"volume":2823,"pages":2824,"note":2825},"Arjovsky2017","Arjovsky, Martin and Chintala, Soumith and Bottou, Léon","Wasserstein generative adversarial networks","International Conference on Machine Learning","PMLR","2017","70","214–223","1-Wasserstein GAN objective; meaningful gradient signal even under disjoint support, where Jensen-Shannon divergence saturates.",{"key":2827,"type":2808,"author":2828,"title":2829,"booktitle":2830,"publisher":2821,"year":2831,"volume":2766,"pages":2832,"note":2833},"Genevay2018","Genevay, Aude and Peyré, Gabriel and Cuturi, Marco","Learning generative models with Sinkhorn divergences","International Conference on Artificial Intelligence and Statistics","2018","1608–1617","Defines the Sinkhorn divergence by debiasing the entropic OT loss; recovers a proper symmetric divergence zero iff distributions match.",{"key":2835,"type":2615,"author":2836,"title":2837,"journal":2838,"year":2831,"volume":2839,"number":2840,"pages":2841,"doi":2842,"note":2843},"Chizat2018","Chizat, Lenaïc and Peyré, Gabriel and Vialard, François-Xavier and Schmitzer, Bernhard","Scaling algorithms for unbalanced optimal transport problems","Mathematics of Computation","87","314","2563–2609","10.1090\u002Fmcom\u002F3303","KL-relaxed marginals for unbalanced OT; softened Sinkhorn iteration handles mass differences and is robust to outliers.",{"key":2845,"type":2615,"author":2846,"title":2847,"journal":2848,"year":2849,"note":2850},"Sejourne2019","Séjourné, Thibault and Feydy, Jean and Vialard, François-Xavier and Trouvé, Alain and Peyré, Gabriel","Sinkhorn divergences for unbalanced optimal transport","arXiv preprint arXiv:1910.12958","2019","Extends the Sinkhorn divergence to unbalanced marginals; convergence analysis in the unbalanced setting.",{"key":2852,"type":2615,"author":2853,"title":2854,"journal":2855,"year":2849,"volume":2856,"number":2767,"pages":2857,"doi":2858,"note":2859},"Schmitzer2019","Schmitzer, Bernhard","Stabilized sparse scaling algorithms for entropy regularized transport problems","SIAM Journal on Scientific Computing","41","A1443–A1481","10.1137\u002F16M1106018","Log-domain (Gibbs-potential) Sinkhorn; numerically stable for all regularisation strengths and cost magnitudes.",{"key":2861,"type":2615,"author":2862,"title":2863,"journal":2708,"year":2864,"volume":2865,"number":422,"pages":2866,"doi":2867,"note":2868},"Orlin1997","Orlin, James B.","A polynomial time primal network simplex algorithm for minimum cost flows","1997","78","109–129","10.1007\u002FBF02614374","Network simplex with polynomial worst-case guarantee; basis for the EXACT\\_EMD backend in torchmatch.",{"key":2870,"type":2615,"author":2871,"title":2872,"journal":2873,"year":2813,"volume":2856,"number":2645,"pages":2874,"doi":2875,"note":2876},"Sejdinovic2013","Sejdinovic, Dino and Sriperumbudur, Bharath and Gretton, Arthur and Fukumizu, Kenji","Equivalence of distance-based and RKHS-based statistics in hypothesis testing","The Annals of Statistics","2263–2291","10.1214\u002F13-AOS1140","Proves that MMD with energy-distance kernels equals an OT-based statistic; the connection between Sinkhorn divergence, OT, and MMD.",{"key":2557,"type":2808,"author":2878,"title":2879,"booktitle":2880,"year":2737,"pages":2881,"doi":2882,"note":2883},"Bewley, Alex and Ge, Zongyuan and Ott, Lionel and Ramos, Fabio and Upcroft, Ben","Simple online and realtime tracking","IEEE International Conference on Image Processing (ICIP)","3464–3468","10.1109\u002FICIP.2016.7533003","SORT; Kalman-filter motion prediction plus IoU cost and Hungarian assignment. Establishes the tracking-by-detection template extended by [Wojke2017], [Zhang2022], [Aharon2022], and [Cao2023].",{"key":2885,"type":2808,"author":2886,"title":2887,"booktitle":2880,"year":2822,"pages":2888,"doi":2889,"note":2890},"Wojke2017","Wojke, Nicolai and Bewley, Alex and Paulus, Dietrich","Simple online and realtime tracking with a deep association metric","3645–3649","10.1109\u002FICIP.2017.8296962","DeepSORT; adds an appearance re-identification embedding distance to the SORT cost matrix in [Bewley2016].",{"key":2561,"type":2808,"author":2892,"title":2893,"booktitle":2894,"year":2895,"volume":2896,"pages":2897,"doi":2898,"note":2899},"Zhang, Yifu and Sun, Peize and Jiang, Yi and Yu, Dongdong and Weng, Fucheng and Yuan, Zehuan and Luo, Ping and Liu, Wenyu and Wang, Xinggang","ByteTrack: Multi-object tracking by associating every detection box","European Conference on Computer Vision (ECCV)","2022","13682","1–21","10.1007\u002F978-3-031-20047-2_1","Associates low-confidence detections through a second Hungarian assignment pass on top of the SORT template.",{"key":2565,"type":2615,"author":2901,"title":2902,"journal":2903,"year":2895,"note":2904},"Aharon, Nir and Orfaig, Roy and Bobrovsky, Ben-Zion","BoT-SORT: Robust associations multi-pedestrian tracking","arXiv preprint arXiv:2206.14651","Camera-motion compensation and appearance embeddings on top of the SORT template; the Hungarian assignment step is unchanged.",{"key":2906,"type":2808,"author":2907,"title":2908,"booktitle":2909,"year":2910,"pages":2911,"doi":2912,"note":2913},"Cao2023","Cao, Jinkun and Pang, Jiangmiao and Weng, Xinshuo and Khirodkar, Rawal and Kitani, Kris","Observation-centric SORT: Rethinking SORT for robust multi-object tracking","IEEE\u002FCVF Conference on Computer Vision and Pattern Recognition (CVPR)","2023","9686–9696","10.1109\u002FCVPR52729.2023.00934","Observation-centric re-update reduces motion-model drift under occlusion; same Hungarian assignment core as [Bewley2016].",{"key":2915,"type":2808,"author":2916,"title":2917,"booktitle":2894,"year":2918,"volume":2919,"pages":2920,"doi":2921,"note":2922},"Carion2020","Carion, Nicolas and Massa, Francisco and Synnaeve, Gabriel and Usunier, Nicolas and Kirillov, Alexander and Zagoruyko, Sergey","End-to-end object detection with transformers","2020","12346","213–229","10.1007\u002F978-3-030-58452-8_13","DETR; introduces the Hungarian-matching set-prediction training loss that the entire DETR lineage, including [Cheng2021], preserves.",{"key":2924,"type":2808,"author":2925,"title":2926,"booktitle":2811,"year":2927,"volume":2928,"pages":2929,"note":2930},"Cheng2021","Cheng, Bowen and Schwing, Alexander G. and Kirillov, Alexander","Per-pixel classification is not all you need for semantic segmentation","2021","34","17864–17875","MaskFormer; recasts semantic segmentation as mask classification, preserving the Hungarian matching step from the DETR lineage ([Carion2020]).",{"key":2932,"type":2615,"author":2933,"title":2934,"journal":2935,"year":2936,"volume":2937,"number":2635,"pages":2938,"doi":2939,"note":2940},"Pitie2007","Pitié, François and Kokaram, Anil C. and Dahyot, Rozenn","Automated colour grading using colour distribution transfer","Computer Vision and Image Understanding","2007","107","123–137","10.1016\u002Fj.cviu.2006.11.011","Iterated 1D OT projections (sliced Wasserstein) for colour transfer; scales to full 3D colour histograms.",{"key":2942,"type":2808,"author":2943,"title":2944,"booktitle":2945,"year":2831,"note":2946},"Tolstikhin2018","Tolstikhin, Ilya and Bousquet, Olivier and Gelly, Sylvain and Schölkopf, Bernhard","Wasserstein auto-encoders","International Conference on Learning Representations","WAE; replaces the VAE evidence lower bound with a Wasserstein distance between the aggregate posterior and the prior.",{"key":2948,"type":2808,"author":2949,"title":2950,"booktitle":2945,"year":2910,"note":2951},"Lipman2022","Lipman, Yaron and Chen, Ricky T. Q. and Ben-Hamu, Heli and Nickel, Maximilian and Le, Matt","Flow matching for generative modeling","Frames diffusion-model training as learning a vector field transporting a noise source distribution to the data target distribution; the OT-conditioned variant uses the OT displacement plan between individual samples.",{"key":2953,"type":2808,"author":2954,"title":2955,"booktitle":2945,"year":2910,"note":2956},"Liu2022","Liu, Xingchao and Gong, Chengyue and Liu, Qiang","Flow straight and fast: Learning to generate and transfer data with rectified flow","Straightens [Lipman2022]-style flow-matching trajectories via an OT-conditioned reflow procedure, reducing inference sampling steps.",{"key":2958,"type":2615,"author":2959,"title":2960,"journal":2961,"year":2822,"volume":2962,"number":2963,"pages":2964,"doi":2965,"note":2966},"Courty2017","Courty, Nicolas and Flamary, Rémi and Tuia, Devis and Rakotomamonjy, Alain","Optimal transport for domain adaptation","IEEE Transactions on Pattern Analysis and Machine Intelligence","39","9","1853–1865","10.1109\u002FTPAMI.2016.2615921","OTDA; transports labelled source features toward an unlabelled target domain via the regularised OT plan, then trains a target-domain classifier on the result.",{"key":2968,"type":2808,"author":2969,"title":2970,"booktitle":2894,"year":2831,"volume":2971,"pages":2972,"doi":2973,"note":2974},"Damodaran2018","Damodaran, Bharath Bhushan and Kellenberger, Benjamin and Flamary, Rémi and Tuia, Devis and Courty, Nicolas","DeepJDOT: Deep joint distribution optimal transport for unsupervised domain adaptation","11208","467–483","10.1007\u002F978-3-030-01225-0_28","Integrates [Courty2017]-style OT alignment into end-to-end training; the OT plan between minibatches is recomputed every iteration and reweights the classification loss.",{"key":2976,"type":2615,"author":2977,"title":2978,"journal":2979,"year":2980,"volume":2981,"number":422,"pages":2982,"doi":2983,"note":2984},"Agueh2011","Agueh, Martial and Carlier, Guillaume","Barycenters in the Wasserstein space","SIAM Journal on Mathematical Analysis","2011","43","904–924","10.1137\u002F100805741","Defines Wasserstein barycenters; weighted averages of distributions that respect the geometry of the ground metric, unlike Euclidean averaging.",{"key":2986,"type":2808,"author":2987,"title":2988,"booktitle":2820,"publisher":2821,"year":2989,"volume":2756,"pages":2990,"note":2991},"Kusner2015","Kusner, Matt J. and Sun, Yu and Kolkin, Nicholas I. and Weinberger, Kilian Q.","From word embeddings to document distances","2015","957–966","Word Mover's Distance; OT between word-embedding distributions of two documents, insensitive to synonymy and paraphrase.",{"key":2993,"type":2615,"author":2994,"title":2995,"journal":2996,"year":2849,"volume":2997,"number":2691,"pages":2998,"doi":2999,"note":3000},"Schiebinger2019","Schiebinger, Geoffrey and Shu, Jian and Tabaka, Marcin and Cleary, Brian and Subramanian, Vidya and Solomon, Aryeh and Gould, Joshua and Liu, Siyan and Lin, Stacie and Berube, Peter and Lee, Lia and Chen, Jenny and Brumbaugh, Justin and Rigollet, Philippe and Hochedlinger, Konrad and Jaenisch, Rudolf and Regev, Aviv and Lander, Eric S.","Optimal-transport analysis of single-cell gene expression identifies developmental trajectories in reprogramming","Cell","176","928–943","10.1016\u002Fj.cell.2019.01.006","Waddington-OT; the OT plan between adjacent-timepoint scRNA-seq profiles recovers developmental trajectories without paired data.",{"key":3002,"type":2615,"author":3003,"title":3004,"journal":3005,"year":2910,"doi":3006,"note":3007},"Klein2023","Klein, Dominik and Palla, Giovanni and Lange, Marius and Klein, Michal and Piran, Zoe and Gander, Manuel and Meng-Papaxanthos, Laetitia and Sterr, Michael and Treutlein, Barbara and Lickert, Heiko and Theis, Fabian J.","Mapping cells through time and space with moscot","bioRxiv","10.1101\u002F2023.05.11.540374","Moscot; scales OT-based single-cell trajectory inference in [Schiebinger2019] to million-cell, multi-omics, and spatial datasets. Later published in Nature (2025).",1785218166690]