{"id":59,"date":"2025-12-24T12:57:59","date_gmt":"2025-12-24T12:57:59","guid":{"rendered":"https:\/\/qc.thelazydreamer.com\/?p=59"},"modified":"2025-12-24T12:58:01","modified_gmt":"2025-12-24T12:58:01","slug":"chapter-6-solving-qubo-with-python","status":"publish","type":"post","link":"https:\/\/qc.thelazydreamer.com\/?p=59","title":{"rendered":"Chapter 6: Solving QUBO with\u00a0Python"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">Goal of this&nbsp;chapter<\/h3>\n\n\n\n<p>By the end of this chapter, you will:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Solve a <strong>QUBO problem using Python<\/strong><\/li>\n\n\n\n<li>See how the <strong>graph intuition (nodes &amp; edges)<\/strong> becomes code<\/li>\n\n\n\n<li>Understand how a solver evaluates <strong>energy<\/strong><\/li>\n\n\n\n<li>Build confidence to experiment on your own<\/li>\n<\/ul>\n\n\n\n<p>No advanced math. No quantum yet. Just practical learning.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Problem We Will Solve&nbsp;(Again)<\/h3>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>Choose exactly one drink<\/em><\/p>\n\n\n\n<p>Options:<\/p>\n\n\n\n<p>Coke ( C )<\/p>\n\n\n\n<p>Milk Tea (M)<\/p>\n\n\n\n<p>Orange Juice (O)<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 1: Define Binary Variables<\/strong><\/h4>\n\n\n\n<p>Each choice becomes a binary variable:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>C = 1 if Coke is chosen, else 0<\/li>\n\n\n\n<li>M = 1 if Milk Tea is chosen, else 0<\/li>\n\n\n\n<li>O = 1 if Orange Juice is chosen, else 0<\/li>\n<\/ul>\n\n\n\n<p>Graph view:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>3 nodes<\/li>\n\n\n\n<li>Each node is ON or OFF<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Step 2: Write the Constraint as&nbsp;QUBO<\/h4>\n\n\n\n<p>Rule:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>Choose exactly one drink<\/em><\/p>\n<\/blockquote>\n\n\n\n<p>QUBO pattern (from Chapter 3):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>(C + M + O \u2212 1)\u00b2<\/code><\/pre>\n\n\n\n<p>Why this works:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>= 0 when exactly one is chosen<\/li>\n\n\n\n<li>0 otherwise<\/li>\n<\/ul>\n\n\n\n<p>This is our <strong>energy function<\/strong>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 3: Expand the&nbsp;Formula<\/h4>\n\n\n\n<p>Expand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C + M + O<br>+ 2(CM + CO + MO)<br>\u2212 2(C + M + O)<br>+ 1<\/code><\/pre>\n\n\n\n<p>Ignore the constant <code>+1<\/code>.<\/p>\n\n\n\n<p>What remains defines:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Node weights (linear terms)<\/li>\n\n\n\n<li>Edge penalties (pair terms)<\/li>\n<\/ul>\n\n\n\n<p>This matches the <strong>fully connected graph<\/strong> from Chapter 4.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 4: Represent QUBO in&nbsp;Python<\/strong><\/h4>\n\n\n\n<p>We store QUBO as a dictionary.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import itertools<br><br># Variable order: C=0, M=1, O=2<br>Q = {<br>    (0, 0): -1,<br>    (1, 1): -1,<br>    (2, 2): -1,<br>    (0, 1): 2,<br>    (0, 2): 2,<br>    (1, 2): 2,<br>}<\/code><\/pre>\n\n\n\n<p>Each entry means:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>(i, i)<\/code> \u2192 node weight<\/li>\n\n\n\n<li><code>(i, j)<\/code> \u2192 edge penalty<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Step 5: Define Energy&nbsp;Function<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>def energy(x):<br>    e = 0<br>    for (i, j), w in Q.items():<br>        e += w * x&#91;i] * x&#91;j]<br>    return e<\/code><\/pre>\n\n\n\n<p>This function:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Takes a solution like <code>(1, 0, 0)<\/code><\/li>\n\n\n\n<li>Calculates total energy<\/li>\n<\/ul>\n\n\n\n<p>This is exactly how solvers \u201csee\u201d your graph.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 6: Brute Force&nbsp;Solve<\/h4>\n\n\n\n<p>Because we only have 3 variables, we can try all combinations.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>best = None<br><br>for x in itertools.product(&#91;0, 1], repeat=3):<br>    e = energy(x)<br>    print(x, e)<br>    if best is None or e &lt; best&#91;1]:<br>        best = (x, e)<br>print(\"Best solution:\", best)<\/code><\/pre>\n\n\n\n<p>You will observe:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Invalid choices \u2192 higher energy<\/li>\n\n\n\n<li>Valid choices \u2192 lowest energy<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Step 7: Interpret the&nbsp;Result<\/h4>\n\n\n\n<p>Valid lowest-energy solutions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>(1, 0, 0)<\/code> \u2192 Coke<\/li>\n\n\n\n<li><code>(0, 1, 0)<\/code> \u2192 Milk Tea<\/li>\n\n\n\n<li><code>(0, 0, 1)<\/code> \u2192 Orange Juice<\/li>\n<\/ul>\n\n\n\n<p>All are equally good.<\/p>\n\n\n\n<p>Why?<\/p>\n\n\n\n<p>Because we added <strong>rules<\/strong>, not <strong>preferences<\/strong>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 8: Add Preferences (Objective)<\/h4>\n\n\n\n<p>Let\u2019s say:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Coke is preferred<\/li>\n\n\n\n<li>Milk Tea is disliked<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>Q&#91;(0, 0)] += -2  # reward Coke<br>Q&#91;(1, 1)] += 3   # penalize Milk Tea<\/code><\/pre>\n\n\n\n<p>Re-run the solver.<\/p>\n\n\n\n<p>Now the energy landscape tilts.<\/p>\n\n\n\n<p>One solution becomes the deepest valley.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">What You Just&nbsp;Learned<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>QUBO becomes simple Python code<\/li>\n\n\n\n<li>Solvers only compare energies<\/li>\n\n\n\n<li>Graph intuition directly matches behavior<\/li>\n\n\n\n<li>You don\u2019t need special tools to start<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Scaling up Step 9: Simulated Annealing (Conceptual)<\/strong><\/h4>\n\n\n\n<p>Brute force helped us <strong><em>see everything<\/em><\/strong>.<\/p>\n\n\n\n<p>But it breaks very quickly.<\/p>\n\n\n\n<p>When variables grow:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Combinations explode<\/li>\n\n\n\n<li>Brute force becomes impossible<\/li>\n<\/ul>\n\n\n\n<p>This is where <strong>Simulated Annealing (SA)<\/strong> enters.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h4 class=\"wp-block-heading\">How Simulated Annealing Works<\/h4>\n\n\n\n<p>From Chapter 5 and the graph view:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>QUBO = energy landscape<\/li>\n\n\n\n<li>Valleys = good solutions<\/li>\n\n\n\n<li>Hills = bad solutions<\/li>\n<\/ul>\n\n\n\n<p>Simulated Annealing:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Starts at a random configuration<\/li>\n\n\n\n<li>Flips one variable (node)<\/li>\n\n\n\n<li>Measures energy change<\/li>\n\n\n\n<li>Sometimes accepts worse moves<\/li>\n\n\n\n<li>Gradually becomes more conservative<\/li>\n<\/ol>\n\n\n\n<p>Early stage:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Explores widely<\/li>\n\n\n\n<li>Jumps across hills<\/li>\n<\/ul>\n\n\n\n<p>Late stage:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Settles into a deep valley<\/li>\n<\/ul>\n\n\n\n<p>This is how solvers <strong>escape local minima<\/strong>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Why This Matters for Real&nbsp;Problems<\/h4>\n\n\n\n<p>Most real QUBO problems:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Have hundreds or thousands of variables<\/li>\n\n\n\n<li>Cannot be solved exactly<\/li>\n\n\n\n<li>Need <em>good<\/em> solutions fast<\/li>\n<\/ul>\n\n\n\n<p>Simulated Annealing:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Trades perfection for speed<\/li>\n\n\n\n<li>Handles noisy, complex graphs<\/li>\n\n\n\n<li>Works well with QUBO structure<\/li>\n<\/ul>\n\n\n\n<p>This is why it is widely used in practice.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h4 class=\"wp-block-heading\">Step 10\u200a\u2014\u200aSimulated Annealing in Python (Hands-On)<\/h4>\n\n\n\n<p>Now let\u2019s see <strong>real code<\/strong>.<\/p>\n\n\n\n<p>We will use a <strong>ready-made solver<\/strong>, just like engineers do in real projects.<\/p>\n\n\n\n<p>Below is an example using <strong>Fixstars Amplify<\/strong> (quantum-inspired, runs on classical hardware).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Install Library<\/strong><\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install amplify<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Define the Same QUBO&nbsp;Problem<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>from amplify import BinarySymbolGenerator, solve<br>from amplify.client import FixstarsClient<br><br># Create binary variables<br>q = BinarySymbolGenerator()<br>C, M, O = q.array(3)<br># Exactly-one constraint<br>qubo = (C + M + O - 1) ** 2<br># Add preferences<br>qubo += -2 * C      # prefer Coke<br>qubo += 3 * M       # avoid Milk Tea<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Run Simulated Annealing Solver<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>client = FixstarsClient()<br>client.parameters.timeout = 1000  # milliseconds<br><br>result = solve(qubo, client)<br>best = result.best<br>print(\"Solution:\", best.values)<br>print(\"Energy:\", best.energy)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Output Interpretation<\/h4>\n\n\n\n<p>Example output:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Solution: [1, 0, 0]<br>Energy: -2.0<\/pre>\n\n\n\n<p>Meaning:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Coke = ON<\/li>\n\n\n\n<li>Milk Tea = OFF<\/li>\n\n\n\n<li>Orange Juice = OFF<\/li>\n<\/ul>\n\n\n\n<p>The solver:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Explored many configurations<\/li>\n\n\n\n<li>Escaped local minima<\/li>\n\n\n\n<li>Found the deepest valley<\/li>\n<\/ul>\n\n\n\n<p>All <strong>without brute force<\/strong>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">What Just&nbsp;Happened<\/h4>\n\n\n\n<p>Under the hood, the solver:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Flipped nodes ON\/OFF<\/li>\n\n\n\n<li>Measured energy change<\/li>\n\n\n\n<li>Sometimes accepted worse moves<\/li>\n\n\n\n<li>Slowly settled into a low-energy state<\/li>\n<\/ol>\n\n\n\n<p>Exactly the <strong>annealing behavior<\/strong> we discussed conceptually.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">What You\u2019ve Achieved So&nbsp;Far<\/h3>\n\n\n\n<p>You can now:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Build QUBO models<\/li>\n\n\n\n<li>Understand them as graphs<\/li>\n\n\n\n<li>Solve small problems exactly<\/li>\n\n\n\n<li>Understand how solvers scale<\/li>\n<\/ul>\n\n\n\n<p>You are no longer \u201creading about QUBO\u201d.<\/p>\n\n\n\n<p>You are <strong>using it<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">What\u2019s Next<\/h3>\n\n\n\n<p>In the next chapter, we will:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Introduce the <strong>Ising model<\/strong><\/li>\n\n\n\n<li>See how QUBO maps to Ising<\/li>\n<\/ul>\n\n\n\n<p>This connects modeling to real solvers and machines.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Goal of this&nbsp;chapter By the end of this chapter, you will: No advanced math. No quantum yet. Just practical learning. Problem We Will Solve&nbsp;(Again) Choose exactly one drink Options: Coke ( C ) Milk Tea (M) Orange Juice (O) Step 1: Define Binary Variables Each choice becomes a binary variable: Graph view: Step 2: Write the Constraint as&nbsp;QUBO Rule: Choose exactly one drink QUBO pattern (from Chapter 3): Why this works: This is our energy function. Step 3: Expand the&nbsp;Formula Expand: Ignore the constant +1. What remains defines: This matches the fully connected graph from Chapter 4. Step 4: Represent QUBO in&nbsp;Python We store QUBO as a dictionary. Each entry means: Step 5: Define Energy&nbsp;Function This function: This is exactly how solvers \u201csee\u201d your graph. Step 6: Brute Force&nbsp;Solve Because we only have 3 variables, we can try all combinations. You will observe: Step 7: Interpret the&nbsp;Result Valid lowest-energy solutions: All are equally good. Why? Because we added rules, not preferences. Step 8: Add Preferences (Objective) Let\u2019s say: Re-run the solver. Now the energy landscape tilts. One solution becomes the deepest valley. What You Just&nbsp;Learned Scaling up Step 9: Simulated Annealing (Conceptual) Brute force helped us see everything. But it breaks very quickly. When variables grow: This is where Simulated Annealing (SA) enters. How Simulated Annealing Works From Chapter 5 and the graph view: Simulated Annealing: Early stage: Late stage: This is how solvers escape local minima. Why This Matters for Real&nbsp;Problems Most real QUBO problems: Simulated Annealing: This is why it is widely used in practice. Step 10\u200a\u2014\u200aSimulated Annealing in Python (Hands-On) Now let\u2019s see real code. We will use a ready-made solver, just like engineers do in real projects. Below is an example using Fixstars Amplify (quantum-inspired, runs on classical hardware). Install Library Define the Same QUBO&nbsp;Problem Run Simulated Annealing Solver Output Interpretation Example output: Solution: [1, 0, 0]Energy: -2.0 Meaning: The solver: All without brute force. What Just&nbsp;Happened Under the hood, the solver: Exactly the annealing behavior we discussed conceptually. What You\u2019ve Achieved So&nbsp;Far You can now: You are no longer \u201creading about QUBO\u201d. You are using it. What\u2019s Next In the next chapter, we will: This connects modeling to real solvers and machines.<\/p>\n","protected":false},"author":1,"featured_media":60,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-59","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"jetpack_featured_media_url":"https:\/\/qc.thelazydreamer.com\/wp-content\/uploads\/2025\/12\/Gemini_Generated_Image_hyw350hyw350hyw3.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/posts\/59","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=59"}],"version-history":[{"count":1,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/posts\/59\/revisions"}],"predecessor-version":[{"id":61,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/posts\/59\/revisions\/61"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=\/wp\/v2\/media\/60"}],"wp:attachment":[{"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=59"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=59"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qc.thelazydreamer.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=59"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}