[{"data":1,"prerenderedAt":455},["ShallowReactive",2],{"dev-\u002Fdeveloper\u002Fexamples\u002Fmax-cut":3},{"id":4,"title":5,"body":6,"description":436,"extension":437,"faq":438,"meta":448,"navigation":449,"noindex":450,"path":451,"seo":452,"stem":453,"__hash__":454},"content\u002Fdeveloper\u002Fexamples\u002Fmax-cut.md","Solve the max-cut problem in Python",{"type":7,"value":8,"toc":430},"minimark",[9,13,29,34,37,41,61,326,331,335,362,375,389,393,416,426],[10,11,5],"h1",{"id":12},"solve-the-max-cut-problem-in-python",[14,15,16,20,21,24,25,28],"p",{},[17,18,19],"strong",{},"Max-cut"," asks you to split a graph's vertices into two sides so that as many\nedges as possible cross between them. It is NP-hard, and it is the canonical\n",[17,22,23],{},"QUBO \u002F Ising"," problem — the one quantum annealers are built for. With\n",[17,26,27],{},"quicopt"," you solve that same formulation classically, in a few lines of\nPython on an ordinary machine.",[30,31,33],"h2",{"id":32},"the-naive-approach","The naive approach",[14,35,36],{},"The usual answers are a greedy or random assignment (fast, but no guarantee on\nquality), or a QAOA tutorial that needs quantum hardware or a simulator. Max-cut\nis a binary quadratic problem — so hand it to a solver built for exactly that.",[30,38,40],{"id":39},"model-it-as-a-qubo","Model it as a QUBO",[14,42,43,44,48,49,52,53,56,57,60],{},"A binary variable ",[45,46,47],"code",{},"x[i]"," puts vertex ",[45,50,51],{},"i"," on side 0 or 1. An edge ",[45,54,55],{},"(i, j)"," is cut\nexactly when its endpoints differ, which ",[45,58,59],{},"x[i] + x[j] - 2·x[i]·x[j]"," captures.\nMaximize the number of cut edges — a quadratic objective over binaries, no\nconstraints:",[62,63,69],"pre",{"className":64,"code":65,"filename":66,"language":67,"meta":68,"style":68},"language-python shiki shiki-themes github-dark","from ortools.math_opt.python import mathopt\nfrom quicopt import Client\nedges = [(0,1),(0,2),(1,2),(1,3),(2,3),(2,4),(3,4)]\nN = 5\nmodel = mathopt.Model(name=\"maxcut\")\nx = [model.add_binary_variable(name=f\"x{i}\") for i in range(N)]\n# each edge contributes 1 to the cut iff its endpoints are on different sides:\nmodel.maximize(sum(x[i] + x[j] - 2 * x[i] * x[j] for (i, j) in edges))\nprint(Client().solve(model).display)\n","maxcut.py","python","",[45,70,71,90,103,180,191,215,265,272,317],{"__ignoreMap":68},[72,73,76,80,84,87],"span",{"class":74,"line":75},"line",1,[72,77,79],{"class":78},"snl16","from",[72,81,83],{"class":82},"s95oV"," ortools.math_opt.python ",[72,85,86],{"class":78},"import",[72,88,89],{"class":82}," mathopt\n",[72,91,93,95,98,100],{"class":74,"line":92},2,[72,94,79],{"class":78},[72,96,97],{"class":82}," quicopt ",[72,99,86],{"class":78},[72,101,102],{"class":82}," Client\n",[72,104,106,109,112,115,119,122,125,128,130,132,135,137,139,141,143,145,147,149,152,154,156,158,160,162,164,166,169,171,173,175,177],{"class":74,"line":105},3,[72,107,108],{"class":82},"edges ",[72,110,111],{"class":78},"=",[72,113,114],{"class":82}," [(",[72,116,118],{"class":117},"sDLfK","0",[72,120,121],{"class":82},",",[72,123,124],{"class":117},"1",[72,126,127],{"class":82},"),(",[72,129,118],{"class":117},[72,131,121],{"class":82},[72,133,134],{"class":117},"2",[72,136,127],{"class":82},[72,138,124],{"class":117},[72,140,121],{"class":82},[72,142,134],{"class":117},[72,144,127],{"class":82},[72,146,124],{"class":117},[72,148,121],{"class":82},[72,150,151],{"class":117},"3",[72,153,127],{"class":82},[72,155,134],{"class":117},[72,157,121],{"class":82},[72,159,151],{"class":117},[72,161,127],{"class":82},[72,163,134],{"class":117},[72,165,121],{"class":82},[72,167,168],{"class":117},"4",[72,170,127],{"class":82},[72,172,151],{"class":117},[72,174,121],{"class":82},[72,176,168],{"class":117},[72,178,179],{"class":82},")]\n",[72,181,183,186,188],{"class":74,"line":182},4,[72,184,185],{"class":82},"N ",[72,187,111],{"class":78},[72,189,190],{"class":117}," 5\n",[72,192,194,197,199,202,206,208,212],{"class":74,"line":193},5,[72,195,196],{"class":82},"model ",[72,198,111],{"class":78},[72,200,201],{"class":82}," mathopt.Model(",[72,203,205],{"class":204},"s9osk","name",[72,207,111],{"class":78},[72,209,211],{"class":210},"sU2Wk","\"maxcut\"",[72,213,214],{"class":82},")\n",[72,216,218,221,223,226,228,230,233,236,239,241,244,247,250,253,256,259,262],{"class":74,"line":217},6,[72,219,220],{"class":82},"x ",[72,222,111],{"class":78},[72,224,225],{"class":82}," [model.add_binary_variable(",[72,227,205],{"class":204},[72,229,111],{"class":78},[72,231,232],{"class":78},"f",[72,234,235],{"class":210},"\"x",[72,237,238],{"class":117},"{",[72,240,51],{"class":82},[72,242,243],{"class":117},"}",[72,245,246],{"class":210},"\"",[72,248,249],{"class":82},") ",[72,251,252],{"class":78},"for",[72,254,255],{"class":82}," i ",[72,257,258],{"class":78},"in",[72,260,261],{"class":117}," range",[72,263,264],{"class":82},"(N)]\n",[72,266,268],{"class":74,"line":267},7,[72,269,271],{"class":270},"sAwPA","# each edge contributes 1 to the cut iff its endpoints are on different sides:\n",[72,273,275,278,281,284,287,290,293,296,299,302,305,307,309,312,314],{"class":74,"line":274},8,[72,276,277],{"class":82},"model.maximize(",[72,279,280],{"class":117},"sum",[72,282,283],{"class":82},"(x[i] ",[72,285,286],{"class":78},"+",[72,288,289],{"class":82}," x[j] ",[72,291,292],{"class":78},"-",[72,294,295],{"class":117}," 2",[72,297,298],{"class":78}," *",[72,300,301],{"class":82}," x[i] ",[72,303,304],{"class":78},"*",[72,306,289],{"class":82},[72,308,252],{"class":78},[72,310,311],{"class":82}," (i, j) ",[72,313,258],{"class":78},[72,315,316],{"class":82}," edges))\n",[72,318,320,323],{"class":74,"line":319},9,[72,321,322],{"class":117},"print",[72,324,325],{"class":82},"(Client().solve(model).display)\n",[327,328],"term-result",{":rows":329,"cmd":330},"[\"├── shots\",\"│   ├── 1 · primal   0    0.0s\",\"│   ├── 2 · primal   5    0.0s  ◀ best\",\"│   ├── 3 · primal   5   1.65s\",\"│   ├── 4 · primal   5    2.0s\",\"│   └── 5 · primal   5    2.0s\",\"├── status:     heuristic\",\"├── feasible:   n\u002Fa\",\"├── objective:  5.0\",\"├── x:          x0=0, x1=1, x2=1, x3=0, x4=1  (5 variables)\",\"└── solve_time: 2.0009 s\"]","$ python maxcut.py",[30,332,334],{"id":333},"what-you-get","What you get",[14,336,337,338,341,342,345,346,349,350,353,354,357,358,361],{},"quicopt runs several heuristic shots and returns the best: a cut of ",[45,339,340],{},"5",", putting\nvertices ",[45,343,344],{},"{1, 2, 4}"," on one side and ",[45,347,348],{},"{0, 3}"," on the other. For this graph that\nis the maximum — brute force over all 2⁵ assignments confirms it. Because a QUBO\nhas no constraints to satisfy, the status is ",[45,351,352],{},"heuristic"," and ",[45,355,356],{},"feasible"," is ",[45,359,360],{},"n\u002Fa",".",[14,363,364,365,367,368,370,371,374],{},"Two seconds for a five-vertex graph looks odd until you read the shots. The first\ncomes back with a cut of ",[45,366,118],{},", the second already has ",[45,369,340],{},", and the rest re-run the\nsearch and confirm it. Without a dual bound there is nothing for the solver to\nstop on, so ",[45,372,373],{},"solve_time"," measures the whole set rather than the moment the answer\nappeared.",[14,376,377,378,382,383,388],{},"The point is that the ",[379,380,381],"em",{},"same"," model scales: on the large public Gset graphs\nquicopt reaches within a fraction of a percent of the best-known cuts in seconds\n— see the ",[384,385,387],"a",{"href":386},"\u002Fbenchmarks\u002Fgset","Gset benchmark"," — without a GPU or a quantum device.",[30,390,392],{"id":391},"next","Next",[394,395,396,403,409],"ul",{},[397,398,399,400],"li",{},"The problem class behind this: ",[384,401,23],{"href":402},"\u002Fproblems\u002Fqubo",[397,404,405,406],{},"Measured results at scale: ",[384,407,408],{"href":386},"Max-cut benchmark (Gset)",[397,410,411,412],{},"Set up the client and solve your first model: ",[384,413,415],{"href":414},"\u002Fdeveloper\u002Fdocs\u002Fquickstart","Getting started",[14,417,418,421,422,425],{},[17,419,420],{},"Reference:"," M. X. Goemans and D. P. Williamson, ",[379,423,424],{},"Improved Approximation\nAlgorithms for Maximum Cut and Satisfiability Problems Using Semidefinite\nProgramming",", Journal of the ACM, 1995.",[427,428,429],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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);}",{"title":68,"searchDepth":92,"depth":92,"links":431},[432,433,434,435],{"id":32,"depth":92,"text":33},{"id":39,"depth":92,"text":40},{"id":333,"depth":92,"text":334},{"id":391,"depth":92,"text":392},"Max-cut asks you to split a graph's vertices into two sides so that as many\nedges as possible cross between them. It is NP-hard, and it is the canonical\nQUBO \u002F Ising problem — the one quantum annealers are built for. With\nquicopt you solve that same formulation classically, in a few lines of\nPython on an ordinary machine.","md",[439,442,445],{"q":440,"a":441},"Isn't max-cut a quantum-computing problem?","Max-cut is the flagship QUBO\u002FIsing problem that quantum annealers and QAOA target. quicopt solves the exact same formulation classically, on a normal CPU — a classical alternative to a quantum computer, no hardware required.",{"q":443,"a":444},"Is the returned cut optimal?","Max-cut is NP-hard, so quicopt returns a heuristic solution (status: heuristic). For the small graph here it finds a cut of 5 — the maximum, confirmed by brute force. On large graphs it targets best-known quality; see the Gset benchmark.",{"q":446,"a":447},"Is quicopt free to use?","Yes, to start: sign up for a free account, install the client with pip install \"quicopt[mathopt]\" and set your API key. The free account solves models with up to 50 variables and has a limited compute allowance; no license is needed.",{},true,false,"\u002Fdeveloper\u002Fexamples\u002Fmax-cut",{"title":5,"description":436},"developer\u002Fexamples\u002Fmax-cut","iGypXdOxh-NaTeS8dv6Vzu3f-fuCVAcaXLLl0IIiqEk",1794337679442]