[{"data":1,"prerenderedAt":2452},["ShallowReactive",2],{"navigation":3,"\u002Falgorithms\u002Fassignment\u002Fquickstart":174,"\u002Falgorithms\u002Fassignment\u002Fquickstart-surround":2447},[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":2441,"extension":2442,"links":177,"meta":2443,"navigation":2444,"path":23,"seo":2445,"stem":24,"__hash__":2446},"docs\u002F2.algorithms\u002F1.assignment\u002F1.quickstart.md","Assignment quickstart",null,{"type":179,"value":180,"toc":2426},"minimark",[181,186,217,236,240,243,249,609,618,622,638,889,892,926,930,936,1272,1281,1288,1297,1546,1551,1555,1558,1711,1718,1722,1736,1917,1921,1938,2051,2065,2070,2077,2149,2153,2163,2309,2314,2384,2396,2400,2422],[182,183,185],"h2",{"id":184},"setup","Setup",[187,188,193],"pre",{"className":189,"code":190,"language":191,"meta":192,"style":192},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","import torch\nimport torchmatch\n","python","",[194,195,196,209],"code",{"__ignoreMap":192},[197,198,201,205],"span",{"class":199,"line":200},"line",1,[197,202,204],{"class":203},"sVHd0","import",[197,206,208],{"class":207},"su5hD"," torch\n",[197,210,212,214],{"class":199,"line":211},2,[197,213,204],{"class":203},[197,215,216],{"class":207}," torchmatch\n",[218,219,220,223,224,227,228,231,232,235],"p",{},[194,221,222],{},"import torchmatch"," eagerly loads the ",[194,225,226],{},"torchmatch.assignment"," sub-package\n(CPU extension, and the CUDA extension when a GPU is present), so\n",[194,229,230],{},"torchmatch.assignment.solve"," and ",[194,233,234],{},"torchmatch.assignment.ops.*"," are\nready as soon as the import returns.",[182,237,239],{"id":238},"a-square-problem","A square problem",[218,241,242],{},"The same example, expressed two ways:",[218,244,245,248],{},[194,246,247],{},"AUTO"," is the default backend selector: it inspects the problem size and device at call time and picks the fastest available op. The direct-op tab shows what you would call explicitly if you wanted to pin a specific kernel.",[250,251,254,451],"code-tabs",{"default":252,"direct-label":253},"solve","jonker_dense",[255,256,257],"template",{"v-slot:solve":192},[187,258,260],{"className":189,"code":259,"language":191,"meta":192,"style":192},"cost = torch.tensor([\n    [4.0, 1.0, 3.0],\n    [2.0, 0.0, 5.0],\n    [3.0, 2.0, 2.0],\n])\n\nrow_to_col = torchmatch.assignment.solve(cost)\n# [1, 0, 2]\ntotal = cost[torch.arange(3), row_to_col].sum().item()\n# 5.0  (the optimal assignment cost)\n",[194,261,262,285,308,328,347,353,360,390,397,445],{"__ignoreMap":192},[197,263,264,267,271,274,278,282],{"class":199,"line":200},[197,265,266],{"class":207},"cost ",[197,268,270],{"class":269},"smGrS","=",[197,272,273],{"class":207}," torch",[197,275,277],{"class":276},"sP7_E",".",[197,279,281],{"class":280},"slqww","tensor",[197,283,284],{"class":276},"([\n",[197,286,287,290,294,297,300,302,305],{"class":199,"line":211},[197,288,289],{"class":276},"    [",[197,291,293],{"class":292},"srdBf","4.0",[197,295,296],{"class":276},",",[197,298,299],{"class":292}," 1.0",[197,301,296],{"class":276},[197,303,304],{"class":292}," 3.0",[197,306,307],{"class":276},"],\n",[197,309,311,313,316,318,321,323,326],{"class":199,"line":310},3,[197,312,289],{"class":276},[197,314,315],{"class":292},"2.0",[197,317,296],{"class":276},[197,319,320],{"class":292}," 0.0",[197,322,296],{"class":276},[197,324,325],{"class":292}," 5.0",[197,327,307],{"class":276},[197,329,331,333,336,338,341,343,345],{"class":199,"line":330},4,[197,332,289],{"class":276},[197,334,335],{"class":292},"3.0",[197,337,296],{"class":276},[197,339,340],{"class":292}," 2.0",[197,342,296],{"class":276},[197,344,340],{"class":292},[197,346,307],{"class":276},[197,348,350],{"class":199,"line":349},5,[197,351,352],{"class":276},"])\n",[197,354,356],{"class":199,"line":355},6,[197,357,359],{"emptyLinePlaceholder":358},true,"\n",[197,361,363,366,368,371,373,377,379,381,384,387],{"class":199,"line":362},7,[197,364,365],{"class":207},"row_to_col ",[197,367,270],{"class":269},[197,369,370],{"class":207}," torchmatch",[197,372,277],{"class":276},[197,374,376],{"class":375},"skxfh","assignment",[197,378,277],{"class":276},[197,380,252],{"class":280},[197,382,383],{"class":276},"(",[197,385,386],{"class":280},"cost",[197,388,389],{"class":276},")\n",[197,391,393],{"class":199,"line":392},8,[197,394,396],{"class":395},"sutJx","# [1, 0, 2]\n",[197,398,400,403,405,408,411,414,416,419,421,424,427,430,433,436,439,442],{"class":199,"line":399},9,[197,401,402],{"class":207},"total ",[197,404,270],{"class":269},[197,406,407],{"class":207}," cost",[197,409,410],{"class":276},"[",[197,412,413],{"class":207},"torch",[197,415,277],{"class":276},[197,417,418],{"class":280},"arange",[197,420,383],{"class":276},[197,422,423],{"class":292},"3",[197,425,426],{"class":276},"),",[197,428,429],{"class":207}," row_to_col",[197,431,432],{"class":276},"].",[197,434,435],{"class":280},"sum",[197,437,438],{"class":276},"().",[197,440,441],{"class":280},"item",[197,443,444],{"class":276},"()\n",[197,446,448],{"class":199,"line":447},10,[197,449,450],{"class":395},"# 5.0  (the optimal assignment cost)\n",[255,452,453],{"v-slot:direct":192},[187,454,456],{"className":189,"code":455,"language":191,"meta":192,"style":192},"cost = torch.tensor([\n    [4.0, 1.0, 3.0],\n    [2.0, 0.0, 5.0],\n    [3.0, 2.0, 2.0],\n])\n\n# AUTO would pick jonker_scalar here (N*M = 9 ≤ 64);\n# pick a specific op when you want to choose the kernel.\nrow_to_col = torchmatch.assignment.ops.jonker_dense(cost)\n# [1, 0, 2]\ntotal = cost[torch.arange(3), row_to_col].sum().item()\n# 5.0  (the optimal assignment cost)\n",[194,457,458,472,488,504,520,524,528,533,538,565,569,604],{"__ignoreMap":192},[197,459,460,462,464,466,468,470],{"class":199,"line":200},[197,461,266],{"class":207},[197,463,270],{"class":269},[197,465,273],{"class":207},[197,467,277],{"class":276},[197,469,281],{"class":280},[197,471,284],{"class":276},[197,473,474,476,478,480,482,484,486],{"class":199,"line":211},[197,475,289],{"class":276},[197,477,293],{"class":292},[197,479,296],{"class":276},[197,481,299],{"class":292},[197,483,296],{"class":276},[197,485,304],{"class":292},[197,487,307],{"class":276},[197,489,490,492,494,496,498,500,502],{"class":199,"line":310},[197,491,289],{"class":276},[197,493,315],{"class":292},[197,495,296],{"class":276},[197,497,320],{"class":292},[197,499,296],{"class":276},[197,501,325],{"class":292},[197,503,307],{"class":276},[197,505,506,508,510,512,514,516,518],{"class":199,"line":330},[197,507,289],{"class":276},[197,509,335],{"class":292},[197,511,296],{"class":276},[197,513,340],{"class":292},[197,515,296],{"class":276},[197,517,340],{"class":292},[197,519,307],{"class":276},[197,521,522],{"class":199,"line":349},[197,523,352],{"class":276},[197,525,526],{"class":199,"line":355},[197,527,359],{"emptyLinePlaceholder":358},[197,529,530],{"class":199,"line":362},[197,531,532],{"class":395},"# AUTO would pick jonker_scalar here (N*M = 9 ≤ 64);\n",[197,534,535],{"class":199,"line":392},[197,536,537],{"class":395},"# pick a specific op when you want to choose the kernel.\n",[197,539,540,542,544,546,548,550,552,555,557,559,561,563],{"class":199,"line":399},[197,541,365],{"class":207},[197,543,270],{"class":269},[197,545,370],{"class":207},[197,547,277],{"class":276},[197,549,376],{"class":375},[197,551,277],{"class":276},[197,553,554],{"class":375},"ops",[197,556,277],{"class":276},[197,558,253],{"class":280},[197,560,383],{"class":276},[197,562,386],{"class":280},[197,564,389],{"class":276},[197,566,567],{"class":199,"line":447},[197,568,396],{"class":395},[197,570,572,574,576,578,580,582,584,586,588,590,592,594,596,598,600,602],{"class":199,"line":571},11,[197,573,402],{"class":207},[197,575,270],{"class":269},[197,577,407],{"class":207},[197,579,410],{"class":276},[197,581,413],{"class":207},[197,583,277],{"class":276},[197,585,418],{"class":280},[197,587,383],{"class":276},[197,589,423],{"class":292},[197,591,426],{"class":276},[197,593,429],{"class":207},[197,595,432],{"class":276},[197,597,435],{"class":280},[197,599,438],{"class":276},[197,601,441],{"class":280},[197,603,444],{"class":276},[197,605,607],{"class":199,"line":606},12,[197,608,450],{"class":395},[218,610,611,614,615,617],{},[194,612,613],{},"[1, 0, 2]"," reads as row 0 → col 1, row 1 → col 0, row 2 → col 2. Both\nentry points (",[194,616,252],{}," and the direct op) return the same row→col mapping.\nThey differ only in whether the caller chooses the kernel.",[182,619,621],{"id":620},"three-cpu-variants","Three CPU variants",[218,623,624,626,627,630,631,634,635,637],{},[194,625,230],{}," picks among ",[194,628,629],{},"jonker_scalar",",\n",[194,632,633],{},"jonker_compact",", and ",[194,636,253],{}," for you. When you want to\nbenchmark or pin a variant, call the op directly. All three return an\noptimal assignment; on tied cost matrices the specific optimum may\ndiffer between ops, but the total cost is identical.",[187,639,641],{"className":189,"code":640,"language":191,"meta":192,"style":192},"torch.manual_seed(0)\ncost = torch.rand(8, 8, dtype=torch.float64)\n\nfor op_name in (\"jonker_scalar\", \"jonker_dense\", \"jonker_compact\"):\n    op = getattr(torchmatch.assignment.ops, op_name)\n    out = op(cost)\n    total = cost[torch.arange(8), out].sum().item()\n    print(f\"{op_name:18s} -> {out.tolist()}  total={total:.4f}\")\n",[194,642,643,659,699,703,746,777,793,829],{"__ignoreMap":192},[197,644,645,647,649,652,654,657],{"class":199,"line":200},[197,646,413],{"class":207},[197,648,277],{"class":276},[197,650,651],{"class":280},"manual_seed",[197,653,383],{"class":276},[197,655,656],{"class":292},"0",[197,658,389],{"class":276},[197,660,661,663,665,667,669,672,674,677,679,682,684,688,690,692,694,697],{"class":199,"line":211},[197,662,266],{"class":207},[197,664,270],{"class":269},[197,666,273],{"class":207},[197,668,277],{"class":276},[197,670,671],{"class":280},"rand",[197,673,383],{"class":276},[197,675,676],{"class":292},"8",[197,678,296],{"class":276},[197,680,681],{"class":292}," 8",[197,683,296],{"class":276},[197,685,687],{"class":686},"s99_P"," dtype",[197,689,270],{"class":269},[197,691,413],{"class":280},[197,693,277],{"class":276},[197,695,696],{"class":375},"float64",[197,698,389],{"class":276},[197,700,701],{"class":199,"line":310},[197,702,359],{"emptyLinePlaceholder":358},[197,704,705,708,711,714,717,721,724,726,728,731,733,735,737,739,741,743],{"class":199,"line":330},[197,706,707],{"class":203},"for",[197,709,710],{"class":207}," op_name ",[197,712,713],{"class":203},"in",[197,715,716],{"class":276}," (",[197,718,720],{"class":719},"sjJ54","\"",[197,722,629],{"class":723},"s_sjI",[197,725,720],{"class":719},[197,727,296],{"class":276},[197,729,730],{"class":719}," \"",[197,732,253],{"class":723},[197,734,720],{"class":719},[197,736,296],{"class":276},[197,738,730],{"class":719},[197,740,633],{"class":723},[197,742,720],{"class":719},[197,744,745],{"class":276},"):\n",[197,747,748,751,753,757,759,762,764,766,768,770,772,775],{"class":199,"line":349},[197,749,750],{"class":207},"    op ",[197,752,270],{"class":269},[197,754,756],{"class":755},"sptTA"," getattr",[197,758,383],{"class":276},[197,760,761],{"class":280},"torchmatch",[197,763,277],{"class":276},[197,765,376],{"class":375},[197,767,277],{"class":276},[197,769,554],{"class":375},[197,771,296],{"class":276},[197,773,774],{"class":280}," op_name",[197,776,389],{"class":276},[197,778,779,782,784,787,789,791],{"class":199,"line":355},[197,780,781],{"class":207},"    out ",[197,783,270],{"class":269},[197,785,786],{"class":280}," op",[197,788,383],{"class":276},[197,790,386],{"class":280},[197,792,389],{"class":276},[197,794,795,798,800,802,804,806,808,810,812,814,816,819,821,823,825,827],{"class":199,"line":362},[197,796,797],{"class":207},"    total ",[197,799,270],{"class":269},[197,801,407],{"class":207},[197,803,410],{"class":276},[197,805,413],{"class":207},[197,807,277],{"class":276},[197,809,418],{"class":280},[197,811,383],{"class":276},[197,813,676],{"class":292},[197,815,426],{"class":276},[197,817,818],{"class":207}," out",[197,820,432],{"class":276},[197,822,435],{"class":280},[197,824,438],{"class":276},[197,826,441],{"class":280},[197,828,444],{"class":276},[197,830,831,834,836,840,842,845,848,851,854,857,859,862,864,867,870,872,875,877,880,883,885,887],{"class":199,"line":392},[197,832,833],{"class":755},"    print",[197,835,383],{"class":276},[197,837,839],{"class":838},"sbsja","f",[197,841,720],{"class":723},[197,843,844],{"class":292},"{",[197,846,847],{"class":280},"op_name",[197,849,850],{"class":838},":18s",[197,852,853],{"class":292},"}",[197,855,856],{"class":723}," -> ",[197,858,844],{"class":292},[197,860,861],{"class":280},"out",[197,863,277],{"class":276},[197,865,866],{"class":280},"tolist",[197,868,869],{"class":276},"()",[197,871,853],{"class":292},[197,873,874],{"class":723},"  total=",[197,876,844],{"class":292},[197,878,879],{"class":280},"total",[197,881,882],{"class":838},":.4f",[197,884,853],{"class":292},[197,886,720],{"class":723},[197,888,389],{"class":276},[218,890,891],{},"Pick by workload:",[893,894,895,904,911],"ul",{},[896,897,898,903],"li",{},[899,900,901],"strong",{},[194,902,629],{},": sequential, no SIMD; consistent performance\nregardless of matrix size or cost distribution.",[896,905,906,910],{},[899,907,908],{},[194,909,253],{},": uses AVX2 SIMD via a flat memory layout; the default\nfor any problem size or cost distribution.",[896,912,913,917,918,921,922,925],{},[899,914,915],{},[194,916,633],{},": uses AVX2 gather instructions; 20 to 30 % faster\nthan ",[194,919,920],{},"dense"," on small-to-medium square problems whose costs vary\ngradually across rows (e.g. distance or IoU matrices, as opposed to\nsparse or integer-tied costs). See\n",[923,924,37],"a",{"href":38}," for the regime where each wins.",[182,927,929],{"id":928},"rectangular-cost-matrices","Rectangular cost matrices",[218,931,932,933,277],{},"Unmatched rows return ",[194,934,935],{},"-1",[250,937,938,1104],{"default":252,"direct-label":253},[255,939,940],{"v-slot:solve":192},[187,941,943],{"className":189,"code":942,"language":191,"meta":192,"style":192},"# Tall: 3 rows, 2 columns. Only 2 rows can match.\ncost = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])\ntorchmatch.assignment.solve(cost).tolist()\n# [0, 1, -1]\n\n# Wide: 2 rows, 3 columns. All 2 rows match.\ncost = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])\ntorchmatch.assignment.solve(cost).tolist()\n# [0, 1]\n",[194,944,945,950,1000,1023,1028,1032,1037,1077,1099],{"__ignoreMap":192},[197,946,947],{"class":199,"line":200},[197,948,949],{"class":395},"# Tall: 3 rows, 2 columns. Only 2 rows can match.\n",[197,951,952,954,956,958,960,962,965,968,970,972,975,978,980,982,985,987,989,992,994,997],{"class":199,"line":211},[197,953,266],{"class":207},[197,955,270],{"class":269},[197,957,273],{"class":207},[197,959,277],{"class":276},[197,961,281],{"class":280},[197,963,964],{"class":276},"([[",[197,966,967],{"class":292},"1.0",[197,969,296],{"class":276},[197,971,340],{"class":292},[197,973,974],{"class":276},"],",[197,976,977],{"class":276}," [",[197,979,335],{"class":292},[197,981,296],{"class":276},[197,983,984],{"class":292}," 4.0",[197,986,974],{"class":276},[197,988,977],{"class":276},[197,990,991],{"class":292},"5.0",[197,993,296],{"class":276},[197,995,996],{"class":292}," 6.0",[197,998,999],{"class":276},"]])\n",[197,1001,1002,1004,1006,1008,1010,1012,1014,1016,1019,1021],{"class":199,"line":310},[197,1003,761],{"class":207},[197,1005,277],{"class":276},[197,1007,376],{"class":375},[197,1009,277],{"class":276},[197,1011,252],{"class":280},[197,1013,383],{"class":276},[197,1015,386],{"class":280},[197,1017,1018],{"class":276},").",[197,1020,866],{"class":280},[197,1022,444],{"class":276},[197,1024,1025],{"class":199,"line":330},[197,1026,1027],{"class":395},"# [0, 1, -1]\n",[197,1029,1030],{"class":199,"line":349},[197,1031,359],{"emptyLinePlaceholder":358},[197,1033,1034],{"class":199,"line":355},[197,1035,1036],{"class":395},"# Wide: 2 rows, 3 columns. All 2 rows match.\n",[197,1038,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075],{"class":199,"line":362},[197,1040,266],{"class":207},[197,1042,270],{"class":269},[197,1044,273],{"class":207},[197,1046,277],{"class":276},[197,1048,281],{"class":280},[197,1050,964],{"class":276},[197,1052,967],{"class":292},[197,1054,296],{"class":276},[197,1056,340],{"class":292},[197,1058,296],{"class":276},[197,1060,304],{"class":292},[197,1062,974],{"class":276},[197,1064,977],{"class":276},[197,1066,293],{"class":292},[197,1068,296],{"class":276},[197,1070,325],{"class":292},[197,1072,296],{"class":276},[197,1074,996],{"class":292},[197,1076,999],{"class":276},[197,1078,1079,1081,1083,1085,1087,1089,1091,1093,1095,1097],{"class":199,"line":392},[197,1080,761],{"class":207},[197,1082,277],{"class":276},[197,1084,376],{"class":375},[197,1086,277],{"class":276},[197,1088,252],{"class":280},[197,1090,383],{"class":276},[197,1092,386],{"class":280},[197,1094,1018],{"class":276},[197,1096,866],{"class":280},[197,1098,444],{"class":276},[197,1100,1101],{"class":199,"line":399},[197,1102,1103],{"class":395},"# [0, 1]\n",[255,1105,1106],{"v-slot:direct":192},[187,1107,1109],{"className":189,"code":1108,"language":191,"meta":192,"style":192},"# Tall: 3 rows, 2 columns. AUTO would still pick jonker_dense\n# (rectangular routes to dense — the jonker_dense op, which handles\n# non-square matrices — regardless of size).\ncost = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])\ntorchmatch.assignment.ops.jonker_dense(cost).tolist()\n# [0, 1, -1]\n\ncost = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])\ntorchmatch.assignment.ops.jonker_dense(cost).tolist()\n# [0, 1]\n",[194,1110,1111,1116,1121,1126,1168,1194,1198,1202,1242,1268],{"__ignoreMap":192},[197,1112,1113],{"class":199,"line":200},[197,1114,1115],{"class":395},"# Tall: 3 rows, 2 columns. AUTO would still pick jonker_dense\n",[197,1117,1118],{"class":199,"line":211},[197,1119,1120],{"class":395},"# (rectangular routes to dense — the jonker_dense op, which handles\n",[197,1122,1123],{"class":199,"line":310},[197,1124,1125],{"class":395},"# non-square matrices — regardless of size).\n",[197,1127,1128,1130,1132,1134,1136,1138,1140,1142,1144,1146,1148,1150,1152,1154,1156,1158,1160,1162,1164,1166],{"class":199,"line":330},[197,1129,266],{"class":207},[197,1131,270],{"class":269},[197,1133,273],{"class":207},[197,1135,277],{"class":276},[197,1137,281],{"class":280},[197,1139,964],{"class":276},[197,1141,967],{"class":292},[197,1143,296],{"class":276},[197,1145,340],{"class":292},[197,1147,974],{"class":276},[197,1149,977],{"class":276},[197,1151,335],{"class":292},[197,1153,296],{"class":276},[197,1155,984],{"class":292},[197,1157,974],{"class":276},[197,1159,977],{"class":276},[197,1161,991],{"class":292},[197,1163,296],{"class":276},[197,1165,996],{"class":292},[197,1167,999],{"class":276},[197,1169,1170,1172,1174,1176,1178,1180,1182,1184,1186,1188,1190,1192],{"class":199,"line":349},[197,1171,761],{"class":207},[197,1173,277],{"class":276},[197,1175,376],{"class":375},[197,1177,277],{"class":276},[197,1179,554],{"class":375},[197,1181,277],{"class":276},[197,1183,253],{"class":280},[197,1185,383],{"class":276},[197,1187,386],{"class":280},[197,1189,1018],{"class":276},[197,1191,866],{"class":280},[197,1193,444],{"class":276},[197,1195,1196],{"class":199,"line":355},[197,1197,1027],{"class":395},[197,1199,1200],{"class":199,"line":362},[197,1201,359],{"emptyLinePlaceholder":358},[197,1203,1204,1206,1208,1210,1212,1214,1216,1218,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240],{"class":199,"line":392},[197,1205,266],{"class":207},[197,1207,270],{"class":269},[197,1209,273],{"class":207},[197,1211,277],{"class":276},[197,1213,281],{"class":280},[197,1215,964],{"class":276},[197,1217,967],{"class":292},[197,1219,296],{"class":276},[197,1221,340],{"class":292},[197,1223,296],{"class":276},[197,1225,304],{"class":292},[197,1227,974],{"class":276},[197,1229,977],{"class":276},[197,1231,293],{"class":292},[197,1233,296],{"class":276},[197,1235,325],{"class":292},[197,1237,296],{"class":276},[197,1239,996],{"class":292},[197,1241,999],{"class":276},[197,1243,1244,1246,1248,1250,1252,1254,1256,1258,1260,1262,1264,1266],{"class":199,"line":399},[197,1245,761],{"class":207},[197,1247,277],{"class":276},[197,1249,376],{"class":375},[197,1251,277],{"class":276},[197,1253,554],{"class":375},[197,1255,277],{"class":276},[197,1257,253],{"class":280},[197,1259,383],{"class":276},[197,1261,386],{"class":280},[197,1263,1018],{"class":276},[197,1265,866],{"class":280},[197,1267,444],{"class":276},[197,1269,1270],{"class":199,"line":447},[197,1271,1103],{"class":395},[218,1273,1274,1275,1278,1279,277],{},"Both entry points return a tensor of length ",[194,1276,1277],{},"nrows",". When there are more\nrows than columns, the leftover rows cannot be matched and their entries\nare ",[194,1280,935],{},[182,1282,1284,1285],{"id":1283},"forbidden-edges-with-inf","Forbidden edges with ",[194,1286,1287],{},"+inf",[218,1289,1290,1291,1293,1294,1296],{},"Mark cells as infeasible by setting them to ",[194,1292,1287],{},". The ops rewrite\n",[194,1295,1287],{}," to a per-call sentinel (a large finite stand-in value that any optimal solver avoids) internally; no preprocessing is needed.",[250,1298,1299,1423],{"default":252,"direct-label":253},[255,1300,1301],{"v-slot:solve":192},[187,1302,1304],{"className":189,"code":1303,"language":191,"meta":192,"style":192},"cost = torch.tensor([\n    [1.0, float(\"inf\"), 5.0],\n    [float(\"inf\"), 2.0, 3.0],\n    [4.0, 6.0, float(\"inf\")],\n])\ntorchmatch.assignment.solve(cost).tolist()\n",[194,1305,1306,1320,1347,1372,1397,1401],{"__ignoreMap":192},[197,1307,1308,1310,1312,1314,1316,1318],{"class":199,"line":200},[197,1309,266],{"class":207},[197,1311,270],{"class":269},[197,1313,273],{"class":207},[197,1315,277],{"class":276},[197,1317,281],{"class":280},[197,1319,284],{"class":276},[197,1321,1322,1324,1326,1328,1332,1334,1336,1339,1341,1343,1345],{"class":199,"line":211},[197,1323,289],{"class":276},[197,1325,967],{"class":292},[197,1327,296],{"class":276},[197,1329,1331],{"class":1330},"sZMiF"," float",[197,1333,383],{"class":276},[197,1335,720],{"class":719},[197,1337,1338],{"class":723},"inf",[197,1340,720],{"class":719},[197,1342,426],{"class":276},[197,1344,325],{"class":292},[197,1346,307],{"class":276},[197,1348,1349,1351,1354,1356,1358,1360,1362,1364,1366,1368,1370],{"class":199,"line":310},[197,1350,289],{"class":276},[197,1352,1353],{"class":1330},"float",[197,1355,383],{"class":276},[197,1357,720],{"class":719},[197,1359,1338],{"class":723},[197,1361,720],{"class":719},[197,1363,426],{"class":276},[197,1365,340],{"class":292},[197,1367,296],{"class":276},[197,1369,304],{"class":292},[197,1371,307],{"class":276},[197,1373,1374,1376,1378,1380,1382,1384,1386,1388,1390,1392,1394],{"class":199,"line":330},[197,1375,289],{"class":276},[197,1377,293],{"class":292},[197,1379,296],{"class":276},[197,1381,996],{"class":292},[197,1383,296],{"class":276},[197,1385,1331],{"class":1330},[197,1387,383],{"class":276},[197,1389,720],{"class":719},[197,1391,1338],{"class":723},[197,1393,720],{"class":719},[197,1395,1396],{"class":276},")],\n",[197,1398,1399],{"class":199,"line":349},[197,1400,352],{"class":276},[197,1402,1403,1405,1407,1409,1411,1413,1415,1417,1419,1421],{"class":199,"line":355},[197,1404,761],{"class":207},[197,1406,277],{"class":276},[197,1408,376],{"class":375},[197,1410,277],{"class":276},[197,1412,252],{"class":280},[197,1414,383],{"class":276},[197,1416,386],{"class":280},[197,1418,1018],{"class":276},[197,1420,866],{"class":280},[197,1422,444],{"class":276},[255,1424,1425],{"v-slot:direct":192},[187,1426,1428],{"className":189,"code":1427,"language":191,"meta":192,"style":192},"cost = torch.tensor([\n    [1.0, float(\"inf\"), 5.0],\n    [float(\"inf\"), 2.0, 3.0],\n    [4.0, 6.0, float(\"inf\")],\n])\ntorchmatch.assignment.ops.jonker_dense(cost).tolist()\n",[194,1429,1430,1444,1468,1492,1516,1520],{"__ignoreMap":192},[197,1431,1432,1434,1436,1438,1440,1442],{"class":199,"line":200},[197,1433,266],{"class":207},[197,1435,270],{"class":269},[197,1437,273],{"class":207},[197,1439,277],{"class":276},[197,1441,281],{"class":280},[197,1443,284],{"class":276},[197,1445,1446,1448,1450,1452,1454,1456,1458,1460,1462,1464,1466],{"class":199,"line":211},[197,1447,289],{"class":276},[197,1449,967],{"class":292},[197,1451,296],{"class":276},[197,1453,1331],{"class":1330},[197,1455,383],{"class":276},[197,1457,720],{"class":719},[197,1459,1338],{"class":723},[197,1461,720],{"class":719},[197,1463,426],{"class":276},[197,1465,325],{"class":292},[197,1467,307],{"class":276},[197,1469,1470,1472,1474,1476,1478,1480,1482,1484,1486,1488,1490],{"class":199,"line":310},[197,1471,289],{"class":276},[197,1473,1353],{"class":1330},[197,1475,383],{"class":276},[197,1477,720],{"class":719},[197,1479,1338],{"class":723},[197,1481,720],{"class":719},[197,1483,426],{"class":276},[197,1485,340],{"class":292},[197,1487,296],{"class":276},[197,1489,304],{"class":292},[197,1491,307],{"class":276},[197,1493,1494,1496,1498,1500,1502,1504,1506,1508,1510,1512,1514],{"class":199,"line":330},[197,1495,289],{"class":276},[197,1497,293],{"class":292},[197,1499,296],{"class":276},[197,1501,996],{"class":292},[197,1503,296],{"class":276},[197,1505,1331],{"class":1330},[197,1507,383],{"class":276},[197,1509,720],{"class":719},[197,1511,1338],{"class":723},[197,1513,720],{"class":719},[197,1515,1396],{"class":276},[197,1517,1518],{"class":199,"line":349},[197,1519,352],{"class":276},[197,1521,1522,1524,1526,1528,1530,1532,1534,1536,1538,1540,1542,1544],{"class":199,"line":355},[197,1523,761],{"class":207},[197,1525,277],{"class":276},[197,1527,376],{"class":375},[197,1529,277],{"class":276},[197,1531,554],{"class":375},[197,1533,277],{"class":276},[197,1535,253],{"class":280},[197,1537,383],{"class":276},[197,1539,386],{"class":280},[197,1541,1018],{"class":276},[197,1543,866],{"class":280},[197,1545,444],{"class":276},[218,1547,1548,1549,277],{},"When forbidden edges make it impossible to match every row to a distinct\ncolumn, the solver reports those rows as ",[194,1550,935],{},[182,1552,1554],{"id":1553},"nan-handling","NaN handling",[218,1556,1557],{},"NaN signals an upstream bug; both entry points reject it explicitly:",[250,1559,1560,1632],{"default":252,"direct-label":253},[255,1561,1562],{"v-slot:solve":192},[187,1563,1565],{"className":189,"code":1564,"language":191,"meta":192,"style":192},"cost = torch.tensor([[1.0, float(\"nan\")], [2.0, 3.0]])\ntorchmatch.assignment.solve(cost)\n# RuntimeError: torchmatch.assignment.solve: cost contains NaN\n",[194,1566,1567,1609,1627],{"__ignoreMap":192},[197,1568,1569,1571,1573,1575,1577,1579,1581,1583,1585,1587,1589,1591,1594,1596,1599,1601,1603,1605,1607],{"class":199,"line":200},[197,1570,266],{"class":207},[197,1572,270],{"class":269},[197,1574,273],{"class":207},[197,1576,277],{"class":276},[197,1578,281],{"class":280},[197,1580,964],{"class":276},[197,1582,967],{"class":292},[197,1584,296],{"class":276},[197,1586,1331],{"class":1330},[197,1588,383],{"class":276},[197,1590,720],{"class":719},[197,1592,1593],{"class":723},"nan",[197,1595,720],{"class":719},[197,1597,1598],{"class":276},")],",[197,1600,977],{"class":276},[197,1602,315],{"class":292},[197,1604,296],{"class":276},[197,1606,304],{"class":292},[197,1608,999],{"class":276},[197,1610,1611,1613,1615,1617,1619,1621,1623,1625],{"class":199,"line":211},[197,1612,761],{"class":207},[197,1614,277],{"class":276},[197,1616,376],{"class":375},[197,1618,277],{"class":276},[197,1620,252],{"class":280},[197,1622,383],{"class":276},[197,1624,386],{"class":280},[197,1626,389],{"class":276},[197,1628,1629],{"class":199,"line":310},[197,1630,1631],{"class":395},"# RuntimeError: torchmatch.assignment.solve: cost contains NaN\n",[255,1633,1634],{"v-slot:direct":192},[187,1635,1637],{"className":189,"code":1636,"language":191,"meta":192,"style":192},"cost = torch.tensor([[1.0, float(\"nan\")], [2.0, 3.0]])\ntorchmatch.assignment.ops.jonker_dense(cost)\n# RuntimeError: lapjv: cost matrix contains NaN. Use +inf for\n# forbidden pairs; NaN signals an upstream bug.\n",[194,1638,1639,1679,1701,1706],{"__ignoreMap":192},[197,1640,1641,1643,1645,1647,1649,1651,1653,1655,1657,1659,1661,1663,1665,1667,1669,1671,1673,1675,1677],{"class":199,"line":200},[197,1642,266],{"class":207},[197,1644,270],{"class":269},[197,1646,273],{"class":207},[197,1648,277],{"class":276},[197,1650,281],{"class":280},[197,1652,964],{"class":276},[197,1654,967],{"class":292},[197,1656,296],{"class":276},[197,1658,1331],{"class":1330},[197,1660,383],{"class":276},[197,1662,720],{"class":719},[197,1664,1593],{"class":723},[197,1666,720],{"class":719},[197,1668,1598],{"class":276},[197,1670,977],{"class":276},[197,1672,315],{"class":292},[197,1674,296],{"class":276},[197,1676,304],{"class":292},[197,1678,999],{"class":276},[197,1680,1681,1683,1685,1687,1689,1691,1693,1695,1697,1699],{"class":199,"line":211},[197,1682,761],{"class":207},[197,1684,277],{"class":276},[197,1686,376],{"class":375},[197,1688,277],{"class":276},[197,1690,554],{"class":375},[197,1692,277],{"class":276},[197,1694,253],{"class":280},[197,1696,383],{"class":276},[197,1698,386],{"class":280},[197,1700,389],{"class":276},[197,1702,1703],{"class":199,"line":310},[197,1704,1705],{"class":395},"# RuntimeError: lapjv: cost matrix contains NaN. Use +inf for\n",[197,1707,1708],{"class":199,"line":330},[197,1709,1710],{"class":395},"# forbidden pairs; NaN signals an upstream bug.\n",[218,1712,1713,1714,1717],{},"Both paths raise ",[194,1715,1716],{},"RuntimeError"," with a message explaining the NaN was found.",[182,1719,1721],{"id":1720},"empty-inputs","Empty inputs",[218,1723,1724,1725,1728,1729,634,1732,1735],{},"Both entry points accept ",[194,1726,1727],{},"(0, 0)",", ",[194,1730,1731],{},"(N, 0)",[194,1733,1734],{},"(0, M)"," inputs:",[250,1737,1738,1826],{"default":252,"direct-label":253},[255,1739,1740],{"v-slot:solve":192},[187,1741,1743],{"className":189,"code":1742,"language":191,"meta":192,"style":192},"torchmatch.assignment.solve(torch.empty(0, 0)).tolist()\n# []\ntorchmatch.assignment.solve(torch.empty(3, 0)).tolist()\n# [-1, -1, -1]\n",[194,1744,1745,1782,1787,1821],{"__ignoreMap":192},[197,1746,1747,1749,1751,1753,1755,1757,1759,1761,1763,1766,1768,1770,1772,1775,1778,1780],{"class":199,"line":200},[197,1748,761],{"class":207},[197,1750,277],{"class":276},[197,1752,376],{"class":375},[197,1754,277],{"class":276},[197,1756,252],{"class":280},[197,1758,383],{"class":276},[197,1760,413],{"class":280},[197,1762,277],{"class":276},[197,1764,1765],{"class":280},"empty",[197,1767,383],{"class":276},[197,1769,656],{"class":292},[197,1771,296],{"class":276},[197,1773,1774],{"class":292}," 0",[197,1776,1777],{"class":276},")).",[197,1779,866],{"class":280},[197,1781,444],{"class":276},[197,1783,1784],{"class":199,"line":211},[197,1785,1786],{"class":395},"# []\n",[197,1788,1789,1791,1793,1795,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815,1817,1819],{"class":199,"line":310},[197,1790,761],{"class":207},[197,1792,277],{"class":276},[197,1794,376],{"class":375},[197,1796,277],{"class":276},[197,1798,252],{"class":280},[197,1800,383],{"class":276},[197,1802,413],{"class":280},[197,1804,277],{"class":276},[197,1806,1765],{"class":280},[197,1808,383],{"class":276},[197,1810,423],{"class":292},[197,1812,296],{"class":276},[197,1814,1774],{"class":292},[197,1816,1777],{"class":276},[197,1818,866],{"class":280},[197,1820,444],{"class":276},[197,1822,1823],{"class":199,"line":330},[197,1824,1825],{"class":395},"# [-1, -1, -1]\n",[255,1827,1828],{"v-slot:direct":192},[187,1829,1831],{"className":189,"code":1830,"language":191,"meta":192,"style":192},"torchmatch.assignment.ops.jonker_dense(torch.empty(0, 0)).tolist()\n# []\ntorchmatch.assignment.ops.jonker_dense(torch.empty(3, 0)).tolist()\n# [-1, -1, -1]\n",[194,1832,1833,1871,1875,1913],{"__ignoreMap":192},[197,1834,1835,1837,1839,1841,1843,1845,1847,1849,1851,1853,1855,1857,1859,1861,1863,1865,1867,1869],{"class":199,"line":200},[197,1836,761],{"class":207},[197,1838,277],{"class":276},[197,1840,376],{"class":375},[197,1842,277],{"class":276},[197,1844,554],{"class":375},[197,1846,277],{"class":276},[197,1848,253],{"class":280},[197,1850,383],{"class":276},[197,1852,413],{"class":280},[197,1854,277],{"class":276},[197,1856,1765],{"class":280},[197,1858,383],{"class":276},[197,1860,656],{"class":292},[197,1862,296],{"class":276},[197,1864,1774],{"class":292},[197,1866,1777],{"class":276},[197,1868,866],{"class":280},[197,1870,444],{"class":276},[197,1872,1873],{"class":199,"line":211},[197,1874,1786],{"class":395},[197,1876,1877,1879,1881,1883,1885,1887,1889,1891,1893,1895,1897,1899,1901,1903,1905,1907,1909,1911],{"class":199,"line":310},[197,1878,761],{"class":207},[197,1880,277],{"class":276},[197,1882,376],{"class":375},[197,1884,277],{"class":276},[197,1886,554],{"class":375},[197,1888,277],{"class":276},[197,1890,253],{"class":280},[197,1892,383],{"class":276},[197,1894,413],{"class":280},[197,1896,277],{"class":276},[197,1898,1765],{"class":280},[197,1900,383],{"class":276},[197,1902,423],{"class":292},[197,1904,296],{"class":276},[197,1906,1774],{"class":292},[197,1908,1777],{"class":276},[197,1910,866],{"class":280},[197,1912,444],{"class":276},[197,1914,1915],{"class":199,"line":330},[197,1916,1825],{"class":395},[182,1918,1920],{"id":1919},"batched-problems","Batched problems",[218,1922,1923,1924,1927,1928,1931,1932,1934,1935,277],{},"Pass a 3-D tensor ",[194,1925,1926],{},"(B, N, M)"," to solve ",[194,1929,1930],{},"B"," independent problems in one call.\n",[194,1933,252],{}," returns ",[194,1936,1937],{},"(B, N)",[187,1939,1941],{"className":189,"code":1940,"language":191,"meta":192,"style":192},"import torch\nimport torchmatch\n\n# 64 problems, each 32×32, on CPU\ncosts = torch.rand(64, 32, 32)\nassignments = torchmatch.assignment.solve(costs)   # (64, 32)\n\n# Check that each batch element got a valid assignment\nassert (assignments >= 0).all()\n",[194,1942,1943,1949,1955,1959,1964,1993,2021,2025,2030],{"__ignoreMap":192},[197,1944,1945,1947],{"class":199,"line":200},[197,1946,204],{"class":203},[197,1948,208],{"class":207},[197,1950,1951,1953],{"class":199,"line":211},[197,1952,204],{"class":203},[197,1954,216],{"class":207},[197,1956,1957],{"class":199,"line":310},[197,1958,359],{"emptyLinePlaceholder":358},[197,1960,1961],{"class":199,"line":330},[197,1962,1963],{"class":395},"# 64 problems, each 32×32, on CPU\n",[197,1965,1966,1969,1971,1973,1975,1977,1979,1982,1984,1987,1989,1991],{"class":199,"line":349},[197,1967,1968],{"class":207},"costs ",[197,1970,270],{"class":269},[197,1972,273],{"class":207},[197,1974,277],{"class":276},[197,1976,671],{"class":280},[197,1978,383],{"class":276},[197,1980,1981],{"class":292},"64",[197,1983,296],{"class":276},[197,1985,1986],{"class":292}," 32",[197,1988,296],{"class":276},[197,1990,1986],{"class":292},[197,1992,389],{"class":276},[197,1994,1995,1998,2000,2002,2004,2006,2008,2010,2012,2015,2018],{"class":199,"line":355},[197,1996,1997],{"class":207},"assignments ",[197,1999,270],{"class":269},[197,2001,370],{"class":207},[197,2003,277],{"class":276},[197,2005,376],{"class":375},[197,2007,277],{"class":276},[197,2009,252],{"class":280},[197,2011,383],{"class":276},[197,2013,2014],{"class":280},"costs",[197,2016,2017],{"class":276},")",[197,2019,2020],{"class":395},"   # (64, 32)\n",[197,2022,2023],{"class":199,"line":362},[197,2024,359],{"emptyLinePlaceholder":358},[197,2026,2027],{"class":199,"line":392},[197,2028,2029],{"class":395},"# Check that each batch element got a valid assignment\n",[197,2031,2032,2035,2037,2039,2042,2044,2046,2049],{"class":199,"line":399},[197,2033,2034],{"class":203},"assert",[197,2036,716],{"class":276},[197,2038,1997],{"class":207},[197,2040,2041],{"class":269},">=",[197,2043,1774],{"class":292},[197,2045,1018],{"class":276},[197,2047,2048],{"class":280},"all",[197,2050,444],{"class":276},[218,2052,2053,2054,2057,2058,2061,2062,1018],{},"The CPU dispatcher uses ",[194,2055,2056],{},"at::parallel_for"," to distribute problems across\nthreads; the CUDA backend for ",[194,2059,2060],{},"jonker_dense_batch"," launches a tiled\nshared-memory kernel (square problems only, ",[194,2063,2064],{},"K ≤ 64",[2066,2067,2069],"h3",{"id":2068},"unpacked-output","Unpacked output",[218,2071,2072,2073,2076],{},"When you need matched pairs and unmatched sets separately, pass ",[194,2074,2075],{},"unpack=True",":",[187,2078,2080],{"className":189,"code":2079,"language":191,"meta":192,"style":192},"matches, unmatched_rows, unmatched_cols, n_matched = torchmatch.assignment.solve(\n    costs, unpack=True,\n)\n# matches[b, :n_matched[b]] = matched (row, col) pairs for batch b\n# unmatched_rows[b], unmatched_cols[b] = unmatched indices\n",[194,2081,2082,2117,2135,2139,2144],{"__ignoreMap":192},[197,2083,2084,2087,2089,2092,2094,2097,2099,2102,2104,2106,2108,2110,2112,2114],{"class":199,"line":200},[197,2085,2086],{"class":207},"matches",[197,2088,296],{"class":276},[197,2090,2091],{"class":207}," unmatched_rows",[197,2093,296],{"class":276},[197,2095,2096],{"class":207}," unmatched_cols",[197,2098,296],{"class":276},[197,2100,2101],{"class":207}," n_matched ",[197,2103,270],{"class":269},[197,2105,370],{"class":207},[197,2107,277],{"class":276},[197,2109,376],{"class":375},[197,2111,277],{"class":276},[197,2113,252],{"class":280},[197,2115,2116],{"class":276},"(\n",[197,2118,2119,2122,2124,2127,2129,2133],{"class":199,"line":211},[197,2120,2121],{"class":280},"    costs",[197,2123,296],{"class":276},[197,2125,2126],{"class":686}," unpack",[197,2128,270],{"class":269},[197,2130,2132],{"class":2131},"s39Yj","True",[197,2134,630],{"class":276},[197,2136,2137],{"class":199,"line":310},[197,2138,389],{"class":276},[197,2140,2141],{"class":199,"line":330},[197,2142,2143],{"class":395},"# matches[b, :n_matched[b]] = matched (row, col) pairs for batch b\n",[197,2145,2146],{"class":199,"line":349},[197,2147,2148],{"class":395},"# unmatched_rows[b], unmatched_cols[b] = unmatched indices\n",[182,2150,2152],{"id":2151},"cuda-ops","CUDA ops",[218,2154,2155,2156,1728,2159,2162],{},"The CUDA ops (",[194,2157,2158],{},"munkres",[194,2160,2161],{},"lawler",") use a different algorithm family from the\nCPU JV variants and are available when a GPU is present:",[187,2164,2166],{"className":189,"code":2165,"language":191,"meta":192,"style":192},"import torch\nimport torchmatch\n\ncost = torch.rand(128, 128, device='cuda')\nrow_to_col = torchmatch.assignment.solve(cost)    # AUTO picks lawler at N=128\n\n# Or pin a specific CUDA op\nrow_to_col = torchmatch.assignment.ops.munkres(cost)\nrow_to_col = torchmatch.assignment.ops.lawler(cost)\n",[194,2167,2168,2174,2180,2184,2223,2248,2252,2257,2283],{"__ignoreMap":192},[197,2169,2170,2172],{"class":199,"line":200},[197,2171,204],{"class":203},[197,2173,208],{"class":207},[197,2175,2176,2178],{"class":199,"line":211},[197,2177,204],{"class":203},[197,2179,216],{"class":207},[197,2181,2182],{"class":199,"line":310},[197,2183,359],{"emptyLinePlaceholder":358},[197,2185,2186,2188,2190,2192,2194,2196,2198,2201,2203,2206,2208,2211,2213,2216,2219,2221],{"class":199,"line":330},[197,2187,266],{"class":207},[197,2189,270],{"class":269},[197,2191,273],{"class":207},[197,2193,277],{"class":276},[197,2195,671],{"class":280},[197,2197,383],{"class":276},[197,2199,2200],{"class":292},"128",[197,2202,296],{"class":276},[197,2204,2205],{"class":292}," 128",[197,2207,296],{"class":276},[197,2209,2210],{"class":686}," device",[197,2212,270],{"class":269},[197,2214,2215],{"class":719},"'",[197,2217,2218],{"class":723},"cuda",[197,2220,2215],{"class":719},[197,2222,389],{"class":276},[197,2224,2225,2227,2229,2231,2233,2235,2237,2239,2241,2243,2245],{"class":199,"line":349},[197,2226,365],{"class":207},[197,2228,270],{"class":269},[197,2230,370],{"class":207},[197,2232,277],{"class":276},[197,2234,376],{"class":375},[197,2236,277],{"class":276},[197,2238,252],{"class":280},[197,2240,383],{"class":276},[197,2242,386],{"class":280},[197,2244,2017],{"class":276},[197,2246,2247],{"class":395},"    # AUTO picks lawler at N=128\n",[197,2249,2250],{"class":199,"line":355},[197,2251,359],{"emptyLinePlaceholder":358},[197,2253,2254],{"class":199,"line":362},[197,2255,2256],{"class":395},"# Or pin a specific CUDA op\n",[197,2258,2259,2261,2263,2265,2267,2269,2271,2273,2275,2277,2279,2281],{"class":199,"line":392},[197,2260,365],{"class":207},[197,2262,270],{"class":269},[197,2264,370],{"class":207},[197,2266,277],{"class":276},[197,2268,376],{"class":375},[197,2270,277],{"class":276},[197,2272,554],{"class":375},[197,2274,277],{"class":276},[197,2276,2158],{"class":280},[197,2278,383],{"class":276},[197,2280,386],{"class":280},[197,2282,389],{"class":276},[197,2284,2285,2287,2289,2291,2293,2295,2297,2299,2301,2303,2305,2307],{"class":199,"line":399},[197,2286,365],{"class":207},[197,2288,270],{"class":269},[197,2290,370],{"class":207},[197,2292,277],{"class":276},[197,2294,376],{"class":375},[197,2296,277],{"class":276},[197,2298,554],{"class":375},[197,2300,277],{"class":276},[197,2302,2161],{"class":280},[197,2304,383],{"class":276},[197,2306,386],{"class":280},[197,2308,389],{"class":276},[218,2310,2311,2312,2076],{},"For batched CUDA, pass a 3-D square tensor with ",[194,2313,2064],{},[187,2315,2317],{"className":189,"code":2316,"language":191,"meta":192,"style":192},"costs_gpu = torch.rand(64, 32, 32, device='cuda')\nassignments = torchmatch.assignment.solve(costs_gpu)   # uses jonker_dense_batch CUDA\n",[194,2318,2319,2358],{"__ignoreMap":192},[197,2320,2321,2324,2326,2328,2330,2332,2334,2336,2338,2340,2342,2344,2346,2348,2350,2352,2354,2356],{"class":199,"line":200},[197,2322,2323],{"class":207},"costs_gpu ",[197,2325,270],{"class":269},[197,2327,273],{"class":207},[197,2329,277],{"class":276},[197,2331,671],{"class":280},[197,2333,383],{"class":276},[197,2335,1981],{"class":292},[197,2337,296],{"class":276},[197,2339,1986],{"class":292},[197,2341,296],{"class":276},[197,2343,1986],{"class":292},[197,2345,296],{"class":276},[197,2347,2210],{"class":686},[197,2349,270],{"class":269},[197,2351,2215],{"class":719},[197,2353,2218],{"class":723},[197,2355,2215],{"class":719},[197,2357,389],{"class":276},[197,2359,2360,2362,2364,2366,2368,2370,2372,2374,2376,2379,2381],{"class":199,"line":211},[197,2361,1997],{"class":207},[197,2363,270],{"class":269},[197,2365,370],{"class":207},[197,2367,277],{"class":276},[197,2369,376],{"class":375},[197,2371,277],{"class":276},[197,2373,252],{"class":280},[197,2375,383],{"class":276},[197,2377,2378],{"class":280},"costs_gpu",[197,2380,2017],{"class":276},[197,2382,2383],{"class":395},"   # uses jonker_dense_batch CUDA\n",[218,2385,2386,2387,2389,2390,2392,2393,2395],{},"The CUDA tiled ",[194,2388,2060],{}," kernel is CUDA-graph-safe (compatible\nwith CUDA graph capture for reduced kernel-launch overhead). ",[194,2391,2158],{}," and\n",[194,2394,2161],{}," are not CUDA-graph-safe because they pause GPU execution to copy an\niteration flag back to the CPU on every step.",[182,2397,2399],{"id":2398},"whats-next","What's next",[893,2401,2402,2408,2417],{},[896,2403,2404,2407],{},[923,2405,2406],{"href":27},"Batched tracking workflow",": detection-to-track\nassociation on a batch of frames.",[896,2409,2410,2413,2414,2416],{},[923,2411,2412],{"href":38},"Choosing the right op",": the decision tree that\n",[194,2415,252],{}," automates.",[896,2418,2419,2421],{},[923,2420,59],{"href":60},": if you need to match probability distributions\nrather than discrete items, handle sources and destinations with different\ntotal weight, or produce a differentiable fractional plan instead of a hard\none-to-one assignment, the transport family is the right tool.",[2423,2424,2425],"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 .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 .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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 .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":192,"searchDepth":310,"depth":310,"links":2427},[2428,2429,2430,2431,2432,2434,2435,2436,2439,2440],{"id":184,"depth":211,"text":185},{"id":238,"depth":211,"text":239},{"id":620,"depth":211,"text":621},{"id":928,"depth":211,"text":929},{"id":1283,"depth":211,"text":2433},"Forbidden edges with +inf",{"id":1553,"depth":211,"text":1554},{"id":1720,"depth":211,"text":1721},{"id":1919,"depth":211,"text":1920,"children":2437},[2438],{"id":2068,"depth":310,"text":2069},{"id":2151,"depth":211,"text":2152},{"id":2398,"depth":211,"text":2399},"Solve one cost matrix on CPU with each JV variant, covering square, rectangular, forbidden-edge, NaN, and empty inputs.","md",{},{"title":22},{"title":176,"description":2441},"UhFSwr89Ey0hISjSheGLgTzfRN1G4Z21XdrGL7dMVOE",[2448,2450],{"title":16,"path":17,"stem":18,"description":2449,"children":-1},"Linear assignment problem solvers — Jonker-Volgenant, Hungarian (Munkres, Lawler), and Greedy — registered as PyTorch custom ops.",{"title":26,"path":27,"stem":28,"description":2451,"children":-1},"Detection-to-track association on a batch of frames using IoU costs, with CPU and CUDA backends and one-pass unpacking.",1785218159644]