[{"data":1,"prerenderedAt":1503},["ShallowReactive",2],{"navigation":3,"\u002Fgetting-started":174,"\u002Fgetting-started-surround":1500},[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":5,"api":176,"body":177,"description":1495,"extension":1496,"links":176,"meta":1497,"navigation":176,"path":6,"seo":1498,"stem":7,"__hash__":1499},"docs\u002F1.getting-started.md",null,{"type":178,"value":179,"toc":1486},"minimark",[180,184,204,216,221,246,249,252,324,328,334,608,612,615,786,793,880,887,891,894,901,1023,1026,1150,1156,1160,1163,1328,1337,1341,1347,1395,1405,1409,1413,1441,1445,1482],[181,182,183],"p",{},"torchmatch is a PyTorch extension providing two families of solvers:",[185,186,187,197],"ul",{},[188,189,190,196],"li",{},[191,192,193],"strong",{},[194,195,16],"a",{"href":17}," — the linear assignment problem (LAP): one-to-one matching\nthat minimises total cost. Used in tracking-by-detection (assigning detector outputs to\nobject tracks across frames), DETR-style set losses (matching model predictions to\nground-truth targets before computing a training loss), and cluster evaluation (measuring\nhow well predicted clusters align with ground-truth labels).",[188,198,199,203],{},[191,200,201],{},[194,202,59],{"href":60}," — optimal transport: computing the minimum-cost way to\ntransform one probability distribution into another. Used for comparing point clouds,\nlearning geometry-aware losses, and aligning feature distributions across datasets\n(domain adaptation).",[181,205,206,207,211,212,215],{},"Both problem families (assignment and transport) are registered as ",[208,209,210],"code",{},"torch.ops.*"," custom ops\n(meaning they work natively with ",[208,213,214],{},"torch.compile"," and the rest of the PyTorch ecosystem).",[217,218,220],"h2",{"id":219},"installation","Installation",[222,223,228],"pre",{"className":224,"code":225,"language":226,"meta":227,"style":227},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pip install torchmatch\n","bash","",[208,229,230],{"__ignoreMap":227},[231,232,235,239,243],"span",{"class":233,"line":234},"line",1,[231,236,238],{"class":237},"sbgvK","pip",[231,240,242],{"class":241},"s_sjI"," install",[231,244,245],{"class":241}," torchmatch\n",[181,247,248],{},"torchmatch ships prebuilt wheels for Python 3.13 with cu126, cu128, and cu130 CUDA variants.\nA CPU-only wheel is also available. If no prebuilt wheel matches your Python\u002FCUDA version,\npip falls back to the source distribution (sdist), which JIT-compiles the C++\u002FCUDA extensions\non first import (takes 30–90 s).",[181,250,251],{},"Requirements:",[253,254,255,270],"table",{},[256,257,258],"thead",{},[259,260,261,264,267],"tr",{},[262,263],"th",{},[262,265,266],{},"Required",[262,268,269],{},"Notes",[271,272,273,284,299,310],"tbody",{},[259,274,275,279,282],{},[276,277,278],"td",{},"Python",[276,280,281],{},"≥ 3.13",[276,283],{},[259,285,286,289,292],{},[276,287,288],{},"PyTorch",[276,290,291],{},"≥ 2.11",[276,293,294,295,298],{},"Must match the wheel variant — e.g., install the ",[208,296,297],{},"+cu128"," wheel with torch built for CUDA 12.8",[259,300,301,304,307],{},[276,302,303],{},"CPU",[276,305,306],{},"x86-64 with AVX2\u002FFMA",[276,308,309],{},"Older CPUs fall back to source compilation on first import (see sdist note above)",[259,311,312,315,318],{},[276,313,314],{},"CUDA",[276,316,317],{},"optional",[276,319,320,323],{},[208,321,322],{},"samples.loss"," (Triton kernels) requires CUDA",[217,325,327],{"id":326},"importing","Importing",[181,329,330,333],{},[208,331,332],{},"import torchmatch"," immediately loads both the assignment and transport sub-packages (no\nseparate import needed for each):",[222,335,339],{"className":336,"code":337,"language":338,"meta":227,"style":227},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","import torch\nimport torchmatch\n\n# Assignment: ready immediately\nrow_to_col = torchmatch.assignment.solve(torch.rand(8, 8))\n\n# Transport matrix face: ready immediately\nlog_plan = torchmatch.transport.matrix.solve(torch.rand(8, 12))\n\n# Transport samples face: CUDA required\nx = torch.randn(512, 3, device='cuda')\ny = torch.randn(512, 3, device='cuda')\nloss = torchmatch.transport.samples.loss(x, y)\n","python",[208,340,341,351,358,365,372,425,430,436,479,484,490,536,572],{"__ignoreMap":227},[231,342,343,347],{"class":233,"line":234},[231,344,346],{"class":345},"sVHd0","import",[231,348,350],{"class":349},"su5hD"," torch\n",[231,352,354,356],{"class":233,"line":353},2,[231,355,346],{"class":345},[231,357,245],{"class":349},[231,359,361],{"class":233,"line":360},3,[231,362,364],{"emptyLinePlaceholder":363},true,"\n",[231,366,368],{"class":233,"line":367},4,[231,369,371],{"class":370},"sutJx","# Assignment: ready immediately\n",[231,373,375,378,382,385,389,393,395,399,402,405,407,410,412,416,419,422],{"class":233,"line":374},5,[231,376,377],{"class":349},"row_to_col ",[231,379,381],{"class":380},"smGrS","=",[231,383,384],{"class":349}," torchmatch",[231,386,388],{"class":387},"sP7_E",".",[231,390,392],{"class":391},"skxfh","assignment",[231,394,388],{"class":387},[231,396,398],{"class":397},"slqww","solve",[231,400,401],{"class":387},"(",[231,403,404],{"class":397},"torch",[231,406,388],{"class":387},[231,408,409],{"class":397},"rand",[231,411,401],{"class":387},[231,413,415],{"class":414},"srdBf","8",[231,417,418],{"class":387},",",[231,420,421],{"class":414}," 8",[231,423,424],{"class":387},"))\n",[231,426,428],{"class":233,"line":427},6,[231,429,364],{"emptyLinePlaceholder":363},[231,431,433],{"class":233,"line":432},7,[231,434,435],{"class":370},"# Transport matrix face: ready immediately\n",[231,437,439,442,444,446,448,451,453,456,458,460,462,464,466,468,470,472,474,477],{"class":233,"line":438},8,[231,440,441],{"class":349},"log_plan ",[231,443,381],{"class":380},[231,445,384],{"class":349},[231,447,388],{"class":387},[231,449,450],{"class":391},"transport",[231,452,388],{"class":387},[231,454,455],{"class":391},"matrix",[231,457,388],{"class":387},[231,459,398],{"class":397},[231,461,401],{"class":387},[231,463,404],{"class":397},[231,465,388],{"class":387},[231,467,409],{"class":397},[231,469,401],{"class":387},[231,471,415],{"class":414},[231,473,418],{"class":387},[231,475,476],{"class":414}," 12",[231,478,424],{"class":387},[231,480,482],{"class":233,"line":481},9,[231,483,364],{"emptyLinePlaceholder":363},[231,485,487],{"class":233,"line":486},10,[231,488,489],{"class":370},"# Transport samples face: CUDA required\n",[231,491,493,496,498,501,503,506,508,511,513,516,518,522,524,528,531,533],{"class":233,"line":492},11,[231,494,495],{"class":349},"x ",[231,497,381],{"class":380},[231,499,500],{"class":349}," torch",[231,502,388],{"class":387},[231,504,505],{"class":397},"randn",[231,507,401],{"class":387},[231,509,510],{"class":414},"512",[231,512,418],{"class":387},[231,514,515],{"class":414}," 3",[231,517,418],{"class":387},[231,519,521],{"class":520},"s99_P"," device",[231,523,381],{"class":380},[231,525,527],{"class":526},"sjJ54","'",[231,529,530],{"class":241},"cuda",[231,532,527],{"class":526},[231,534,535],{"class":387},")\n",[231,537,539,542,544,546,548,550,552,554,556,558,560,562,564,566,568,570],{"class":233,"line":538},12,[231,540,541],{"class":349},"y ",[231,543,381],{"class":380},[231,545,500],{"class":349},[231,547,388],{"class":387},[231,549,505],{"class":397},[231,551,401],{"class":387},[231,553,510],{"class":414},[231,555,418],{"class":387},[231,557,515],{"class":414},[231,559,418],{"class":387},[231,561,521],{"class":520},[231,563,381],{"class":380},[231,565,527],{"class":526},[231,567,530],{"class":241},[231,569,527],{"class":526},[231,571,535],{"class":387},[231,573,575,578,580,582,584,586,588,591,593,596,598,601,603,606],{"class":233,"line":574},13,[231,576,577],{"class":349},"loss ",[231,579,381],{"class":380},[231,581,384],{"class":349},[231,583,388],{"class":387},[231,585,450],{"class":391},[231,587,388],{"class":387},[231,589,590],{"class":391},"samples",[231,592,388],{"class":387},[231,594,595],{"class":397},"loss",[231,597,401],{"class":387},[231,599,600],{"class":397},"x",[231,602,418],{"class":387},[231,604,605],{"class":397}," y",[231,607,535],{"class":387},[217,609,611],{"id":610},"first-assignment","First assignment",[181,613,614],{},"Solve a single 8 × 8 cost matrix:",[222,616,618],{"className":336,"code":617,"language":338,"meta":227,"style":227},"import torch\nimport torchmatch\n\ncost = torch.tensor([\n    [4.0, 1.0, 3.0],\n    [2.0, 0.0, 5.0],\n    [3.0, 2.0, 2.0],\n])\nrow_to_col = torchmatch.assignment.solve(cost)      # [1, 0, 2]\nprint(cost[torch.arange(3), row_to_col].sum())      # 4.0, the optimum\n",[208,619,620,626,632,636,653,674,693,711,716,743],{"__ignoreMap":227},[231,621,622,624],{"class":233,"line":234},[231,623,346],{"class":345},[231,625,350],{"class":349},[231,627,628,630],{"class":233,"line":353},[231,629,346],{"class":345},[231,631,245],{"class":349},[231,633,634],{"class":233,"line":360},[231,635,364],{"emptyLinePlaceholder":363},[231,637,638,641,643,645,647,650],{"class":233,"line":367},[231,639,640],{"class":349},"cost ",[231,642,381],{"class":380},[231,644,500],{"class":349},[231,646,388],{"class":387},[231,648,649],{"class":397},"tensor",[231,651,652],{"class":387},"([\n",[231,654,655,658,661,663,666,668,671],{"class":233,"line":374},[231,656,657],{"class":387},"    [",[231,659,660],{"class":414},"4.0",[231,662,418],{"class":387},[231,664,665],{"class":414}," 1.0",[231,667,418],{"class":387},[231,669,670],{"class":414}," 3.0",[231,672,673],{"class":387},"],\n",[231,675,676,678,681,683,686,688,691],{"class":233,"line":427},[231,677,657],{"class":387},[231,679,680],{"class":414},"2.0",[231,682,418],{"class":387},[231,684,685],{"class":414}," 0.0",[231,687,418],{"class":387},[231,689,690],{"class":414}," 5.0",[231,692,673],{"class":387},[231,694,695,697,700,702,705,707,709],{"class":233,"line":432},[231,696,657],{"class":387},[231,698,699],{"class":414},"3.0",[231,701,418],{"class":387},[231,703,704],{"class":414}," 2.0",[231,706,418],{"class":387},[231,708,704],{"class":414},[231,710,673],{"class":387},[231,712,713],{"class":233,"line":438},[231,714,715],{"class":387},"])\n",[231,717,718,720,722,724,726,728,730,732,734,737,740],{"class":233,"line":481},[231,719,377],{"class":349},[231,721,381],{"class":380},[231,723,384],{"class":349},[231,725,388],{"class":387},[231,727,392],{"class":391},[231,729,388],{"class":387},[231,731,398],{"class":397},[231,733,401],{"class":387},[231,735,736],{"class":397},"cost",[231,738,739],{"class":387},")",[231,741,742],{"class":370},"      # [1, 0, 2]\n",[231,744,745,749,751,753,756,758,760,763,765,768,771,774,777,780,783],{"class":233,"line":486},[231,746,748],{"class":747},"sptTA","print",[231,750,401],{"class":387},[231,752,736],{"class":397},[231,754,755],{"class":387},"[",[231,757,404],{"class":397},[231,759,388],{"class":387},[231,761,762],{"class":397},"arange",[231,764,401],{"class":387},[231,766,767],{"class":414},"3",[231,769,770],{"class":387},"),",[231,772,773],{"class":397}," row_to_col",[231,775,776],{"class":387},"].",[231,778,779],{"class":397},"sum",[231,781,782],{"class":387},"())",[231,784,785],{"class":370},"      # 4.0, the optimum\n",[181,787,788,789,792],{},"Unmatched rows (rectangular input, N > M) return ",[208,790,791],{},"−1",":",[222,794,796],{"className":336,"code":795,"language":338,"meta":227,"style":227},"cost = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])\nprint(torchmatch.assignment.solve(cost).tolist())   # [0, 1, -1]\n",[208,797,798,848],{"__ignoreMap":227},[231,799,800,802,804,806,808,810,813,816,818,820,823,826,828,830,833,835,837,840,842,845],{"class":233,"line":234},[231,801,640],{"class":349},[231,803,381],{"class":380},[231,805,500],{"class":349},[231,807,388],{"class":387},[231,809,649],{"class":397},[231,811,812],{"class":387},"([[",[231,814,815],{"class":414},"1.0",[231,817,418],{"class":387},[231,819,704],{"class":414},[231,821,822],{"class":387},"],",[231,824,825],{"class":387}," [",[231,827,699],{"class":414},[231,829,418],{"class":387},[231,831,832],{"class":414}," 4.0",[231,834,822],{"class":387},[231,836,825],{"class":387},[231,838,839],{"class":414},"5.0",[231,841,418],{"class":387},[231,843,844],{"class":414}," 6.0",[231,846,847],{"class":387},"]])\n",[231,849,850,852,854,857,859,861,863,865,867,869,872,875,877],{"class":233,"line":353},[231,851,748],{"class":747},[231,853,401],{"class":387},[231,855,856],{"class":397},"torchmatch",[231,858,388],{"class":387},[231,860,392],{"class":391},[231,862,388],{"class":387},[231,864,398],{"class":397},[231,866,401],{"class":387},[231,868,736],{"class":397},[231,870,871],{"class":387},").",[231,873,874],{"class":397},"tolist",[231,876,782],{"class":387},[231,878,879],{"class":370},"   # [0, 1, -1]\n",[181,881,882,883,886],{},"See the ",[194,884,885],{"href":23},"Assignment quickstart"," for batched problems, unpacking, and\ndirect op access.",[217,888,890],{"id":889},"first-transport-solve","First transport solve",[181,892,893],{},"Compute a transport plan between two distributions represented as a cost matrix\n(entropy-regularised, which makes the problem smooth and differentiable):",[181,895,896,897,900],{},"The result is returned in log space for numerical stability; call ",[208,898,899],{},".exp()"," to recover the\nactual transport plan.",[222,902,904],{"className":336,"code":903,"language":338,"meta":227,"style":227},"import torch\nimport torchmatch\n\ncost = torch.rand(8, 12)                            # (source, target) cost\nlog_plan = torchmatch.transport.matrix.solve(cost)  # (8, 12) in log space\nplan = log_plan.exp()\nplan.backward(torch.ones_like(plan))               # differentiable\n",[208,905,906,912,918,922,947,976,994],{"__ignoreMap":227},[231,907,908,910],{"class":233,"line":234},[231,909,346],{"class":345},[231,911,350],{"class":349},[231,913,914,916],{"class":233,"line":353},[231,915,346],{"class":345},[231,917,245],{"class":349},[231,919,920],{"class":233,"line":360},[231,921,364],{"emptyLinePlaceholder":363},[231,923,924,926,928,930,932,934,936,938,940,942,944],{"class":233,"line":367},[231,925,640],{"class":349},[231,927,381],{"class":380},[231,929,500],{"class":349},[231,931,388],{"class":387},[231,933,409],{"class":397},[231,935,401],{"class":387},[231,937,415],{"class":414},[231,939,418],{"class":387},[231,941,476],{"class":414},[231,943,739],{"class":387},[231,945,946],{"class":370},"                            # (source, target) cost\n",[231,948,949,951,953,955,957,959,961,963,965,967,969,971,973],{"class":233,"line":374},[231,950,441],{"class":349},[231,952,381],{"class":380},[231,954,384],{"class":349},[231,956,388],{"class":387},[231,958,450],{"class":391},[231,960,388],{"class":387},[231,962,455],{"class":391},[231,964,388],{"class":387},[231,966,398],{"class":397},[231,968,401],{"class":387},[231,970,736],{"class":397},[231,972,739],{"class":387},[231,974,975],{"class":370},"  # (8, 12) in log space\n",[231,977,978,981,983,986,988,991],{"class":233,"line":427},[231,979,980],{"class":349},"plan ",[231,982,381],{"class":380},[231,984,985],{"class":349}," log_plan",[231,987,388],{"class":387},[231,989,990],{"class":397},"exp",[231,992,993],{"class":387},"()\n",[231,995,996,999,1001,1004,1006,1008,1010,1013,1015,1017,1020],{"class":233,"line":432},[231,997,998],{"class":349},"plan",[231,1000,388],{"class":387},[231,1002,1003],{"class":397},"backward",[231,1005,401],{"class":387},[231,1007,404],{"class":397},[231,1009,388],{"class":387},[231,1011,1012],{"class":397},"ones_like",[231,1014,401],{"class":387},[231,1016,998],{"class":397},[231,1018,1019],{"class":387},"))",[231,1021,1022],{"class":370},"               # differentiable\n",[181,1024,1025],{},"Compute an optimal-transport loss (Wasserstein distance) between two point clouds — a scalar\nmeasuring how far apart the two sets of points are as distributions (CUDA required):",[222,1027,1029],{"className":336,"code":1028,"language":338,"meta":227,"style":227},"x = torch.randn(512, 3, device='cuda', requires_grad=True)\ny = torch.randn(512, 3, device='cuda')\nloss = torchmatch.transport.samples.loss(x, y)\nloss.backward()\n",[208,1030,1031,1076,1110,1140],{"__ignoreMap":227},[231,1032,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061,1063,1065,1068,1070,1074],{"class":233,"line":234},[231,1034,495],{"class":349},[231,1036,381],{"class":380},[231,1038,500],{"class":349},[231,1040,388],{"class":387},[231,1042,505],{"class":397},[231,1044,401],{"class":387},[231,1046,510],{"class":414},[231,1048,418],{"class":387},[231,1050,515],{"class":414},[231,1052,418],{"class":387},[231,1054,521],{"class":520},[231,1056,381],{"class":380},[231,1058,527],{"class":526},[231,1060,530],{"class":241},[231,1062,527],{"class":526},[231,1064,418],{"class":387},[231,1066,1067],{"class":520}," requires_grad",[231,1069,381],{"class":380},[231,1071,1073],{"class":1072},"s39Yj","True",[231,1075,535],{"class":387},[231,1077,1078,1080,1082,1084,1086,1088,1090,1092,1094,1096,1098,1100,1102,1104,1106,1108],{"class":233,"line":353},[231,1079,541],{"class":349},[231,1081,381],{"class":380},[231,1083,500],{"class":349},[231,1085,388],{"class":387},[231,1087,505],{"class":397},[231,1089,401],{"class":387},[231,1091,510],{"class":414},[231,1093,418],{"class":387},[231,1095,515],{"class":414},[231,1097,418],{"class":387},[231,1099,521],{"class":520},[231,1101,381],{"class":380},[231,1103,527],{"class":526},[231,1105,530],{"class":241},[231,1107,527],{"class":526},[231,1109,535],{"class":387},[231,1111,1112,1114,1116,1118,1120,1122,1124,1126,1128,1130,1132,1134,1136,1138],{"class":233,"line":360},[231,1113,577],{"class":349},[231,1115,381],{"class":380},[231,1117,384],{"class":349},[231,1119,388],{"class":387},[231,1121,450],{"class":391},[231,1123,388],{"class":387},[231,1125,590],{"class":391},[231,1127,388],{"class":387},[231,1129,595],{"class":397},[231,1131,401],{"class":387},[231,1133,600],{"class":397},[231,1135,418],{"class":387},[231,1137,605],{"class":397},[231,1139,535],{"class":387},[231,1141,1142,1144,1146,1148],{"class":233,"line":367},[231,1143,595],{"class":349},[231,1145,388],{"class":387},[231,1147,1003],{"class":397},[231,1149,993],{"class":387},[181,1151,882,1152,1155],{},[194,1153,1154],{"href":65},"Transport quickstart"," for regularisation strength, debiasing\n(a correction that removes entropy artifacts from the plan), and backend selection.",[217,1157,1159],{"id":1158},"op-namespaces","Op namespaces",[181,1161,1162],{},"Every op binds at two locations:",[222,1164,1166],{"className":336,"code":1165,"language":338,"meta":227,"style":227},"# Python-friendly (autocomplete, type-checks)\ntorchmatch.assignment.ops.jonker_dense(cost)\ntorchmatch.transport.matrix.ops.log_sinkhorn(cost, 0.1, 100, None, None, None, None)\n\n# torch.ops namespace (inside torch.compile, dynamic dispatch)\ntorch.ops.assignment.jonker_dense(cost)\ntorch.ops.transport.log_sinkhorn(cost, 0.1, 100, None, None, None, None)\n",[208,1167,1168,1173,1197,1251,1255,1260,1282],{"__ignoreMap":227},[231,1169,1170],{"class":233,"line":234},[231,1171,1172],{"class":370},"# Python-friendly (autocomplete, type-checks)\n",[231,1174,1175,1177,1179,1181,1183,1186,1188,1191,1193,1195],{"class":233,"line":353},[231,1176,856],{"class":349},[231,1178,388],{"class":387},[231,1180,392],{"class":391},[231,1182,388],{"class":387},[231,1184,1185],{"class":391},"ops",[231,1187,388],{"class":387},[231,1189,1190],{"class":397},"jonker_dense",[231,1192,401],{"class":387},[231,1194,736],{"class":397},[231,1196,535],{"class":387},[231,1198,1199,1201,1203,1205,1207,1209,1211,1213,1215,1218,1220,1222,1224,1227,1229,1232,1234,1237,1239,1241,1243,1245,1247,1249],{"class":233,"line":360},[231,1200,856],{"class":349},[231,1202,388],{"class":387},[231,1204,450],{"class":391},[231,1206,388],{"class":387},[231,1208,455],{"class":391},[231,1210,388],{"class":387},[231,1212,1185],{"class":391},[231,1214,388],{"class":387},[231,1216,1217],{"class":397},"log_sinkhorn",[231,1219,401],{"class":387},[231,1221,736],{"class":397},[231,1223,418],{"class":387},[231,1225,1226],{"class":414}," 0.1",[231,1228,418],{"class":387},[231,1230,1231],{"class":414}," 100",[231,1233,418],{"class":387},[231,1235,1236],{"class":1072}," None",[231,1238,418],{"class":387},[231,1240,1236],{"class":1072},[231,1242,418],{"class":387},[231,1244,1236],{"class":1072},[231,1246,418],{"class":387},[231,1248,1236],{"class":1072},[231,1250,535],{"class":387},[231,1252,1253],{"class":233,"line":367},[231,1254,364],{"emptyLinePlaceholder":363},[231,1256,1257],{"class":233,"line":374},[231,1258,1259],{"class":370},"# torch.ops namespace (inside torch.compile, dynamic dispatch)\n",[231,1261,1262,1264,1266,1268,1270,1272,1274,1276,1278,1280],{"class":233,"line":427},[231,1263,404],{"class":349},[231,1265,388],{"class":387},[231,1267,1185],{"class":391},[231,1269,388],{"class":387},[231,1271,392],{"class":391},[231,1273,388],{"class":387},[231,1275,1190],{"class":397},[231,1277,401],{"class":387},[231,1279,736],{"class":397},[231,1281,535],{"class":387},[231,1283,1284,1286,1288,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310,1312,1314,1316,1318,1320,1322,1324,1326],{"class":233,"line":432},[231,1285,404],{"class":349},[231,1287,388],{"class":387},[231,1289,1185],{"class":391},[231,1291,388],{"class":387},[231,1293,450],{"class":391},[231,1295,388],{"class":387},[231,1297,1217],{"class":397},[231,1299,401],{"class":387},[231,1301,736],{"class":397},[231,1303,418],{"class":387},[231,1305,1226],{"class":414},[231,1307,418],{"class":387},[231,1309,1231],{"class":414},[231,1311,418],{"class":387},[231,1313,1236],{"class":1072},[231,1315,418],{"class":387},[231,1317,1236],{"class":1072},[231,1319,418],{"class":387},[231,1321,1236],{"class":1072},[231,1323,418],{"class":387},[231,1325,1236],{"class":1072},[231,1327,535],{"class":387},[181,1329,1330,1331,1334,1335,388],{},"Both forms are identical objects: ",[208,1332,1333],{},"torchmatch.assignment.ops.jonker_dense is torch.ops.assignment.jonker_dense"," is ",[208,1336,1073],{},[217,1338,1340],{"id":1339},"development-environment-nix","Development environment (Nix)",[181,1342,1343,1344,792],{},"The repo ships a self-contained ",[208,1345,1346],{},"flake.nix",[222,1348,1350],{"className":224,"code":1349,"language":226,"meta":227,"style":227},"nix develop                   # default = cu128\nnix develop .#cpu             # CPU-only\nnix develop .#cu126 \u002F .#cu128 \u002F .#cu130\n",[208,1351,1352,1363,1375],{"__ignoreMap":227},[231,1353,1354,1357,1360],{"class":233,"line":234},[231,1355,1356],{"class":237},"nix",[231,1358,1359],{"class":241}," develop",[231,1361,1362],{"class":370},"                   # default = cu128\n",[231,1364,1365,1367,1369,1372],{"class":233,"line":353},[231,1366,1356],{"class":237},[231,1368,1359],{"class":241},[231,1370,1371],{"class":241}," .#cpu",[231,1373,1374],{"class":370},"             # CPU-only\n",[231,1376,1377,1379,1381,1384,1387,1390,1392],{"class":233,"line":360},[231,1378,1356],{"class":237},[231,1380,1359],{"class":241},[231,1382,1383],{"class":241}," .#cu126",[231,1385,1386],{"class":241}," \u002F",[231,1388,1389],{"class":241}," .#cu128",[231,1391,1386],{"class":241},[231,1393,1394],{"class":241}," .#cu130\n",[181,1396,1397,1398,1401,1402,388],{},"Inside the shell: ",[208,1399,1400],{},"uv sync --extra cu128 --all-groups"," then ",[208,1403,1404],{},"nix run .#test",[217,1406,1408],{"id":1407},"next-steps","Next steps",[181,1410,1411],{},[191,1412,16],{},[185,1414,1415,1420,1426,1431,1436],{},[188,1416,1417,1419],{},[194,1418,22],{"href":23}," — batched problems, unpacking, backends",[188,1421,1422,1425],{},[194,1423,1424],{"href":27},"Tracking tutorial"," — end-to-end SORT-style tracking",[188,1427,1428,1430],{},[194,1429,9],{"href":30}," — JV, Munkres, Lawler derivations",[188,1432,1433,1435],{},[194,1434,33],{"href":34}," — op signatures and constraints",[188,1437,1438,1440],{},[194,1439,37],{"href":38}," — decision tree from benchmarks",[181,1442,1443],{},[191,1444,59],{},[185,1446,1447,1458,1463,1468,1477],{},[188,1448,1449,1451,1452,1455,1456],{},[194,1450,22],{"href":65}," — ",[208,1453,1454],{},"matrix.solve"," and ",[208,1457,322],{},[188,1459,1460,1462],{},[194,1461,68],{"href":69}," — Wasserstein training loss",[188,1464,1465,1467],{},[194,1466,9],{"href":72}," — Sinkhorn, debiasing, unbalanced OT",[188,1469,1470,1451,1472,1455,1474,1476],{},[194,1471,33],{"href":75},[208,1473,1454],{},[208,1475,322],{}," signatures",[188,1478,1479,1481],{},[194,1480,37],{"href":78}," — backend selection guide",[1483,1484,1485],"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 .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 .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 .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--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 .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 .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 .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":227,"searchDepth":360,"depth":360,"links":1487},[1488,1489,1490,1491,1492,1493,1494],{"id":219,"depth":353,"text":220},{"id":326,"depth":353,"text":327},{"id":610,"depth":353,"text":611},{"id":889,"depth":353,"text":890},{"id":1158,"depth":353,"text":1159},{"id":1339,"depth":353,"text":1340},{"id":1407,"depth":353,"text":1408},"Install torchmatch, load the extensions, and run your first assignment and transport ops.","md",{},{"title":5,"description":1495},"h7IFgpxiJ-lO9vA0clwmSZwsCIYvO5mUcmrHhHh_VG4",[176,1501],{"title":9,"path":10,"stem":14,"description":1502,"children":-1},"The two solver families torchmatch provides — linear assignment (one-to-one matching) and optimal transport (soft, many-to-many matching) — each with quickstarts, algorithm notes, and a full op reference.",1785218159644]