44 "cell_type" : " markdown" ,
55 "metadata" : {},
66 "source" : [
7- " ## QAOA Mixer Comparison: Vanilla, Free, and Orbit\n " ,
7+ " ## QAOA Ansatz Comparison: Vanilla, Free, and Orbit\n " ,
88 " \n " ,
9- " This notebook compares three QAOA variants on the **house graph** (5 nodes, 6 edges): \n " ,
9+ " This notebook compares three QAOA ansätze on the **house graph** (5 nodes, 6 edges). \n " ,
1010 " \n " ,
11- " | Variant | Problem | Mixer | \u03b3 / layer | \u03b2 / layer |\n " ,
12- " |--------- |---------|-------|-----------|----------|\n " ,
11+ " | Ansatz | Problem | Mixer | γ / layer | β / layer |\n " ,
12+ " |--------|---------|-------|-----------|----------|\n " ,
1313 " | **Vanilla** | `MaxCut` | `X` | 1 | 1 |\n " ,
14- " | **Free** | `MaxCut ` | `XMultiAngle` | 1 | N (one per qubit ) |\n " ,
15- " | **Orbit** | `MaxCutOrbit` | `X ` | K (one per edge orbit) | 1 |\n " ,
14+ " | **Free** | `MaxCutFree ` | `XMultiAngle` | E (one per edge) | N (one per node ) |\n " ,
15+ " | **Orbit** | `MaxCutOrbit` | `XOrbit ` | K_E (one per edge orbit) | K_N (one per node orbit) |\n " ,
1616 " \n " ,
17- " - **Vanilla QAOA** uses the standard X mixer with a single shared \u03b3 and \u03b2 per layer.\n " ,
18- " - **Free QAOA** (multi-angle) gives each qubit its own independent \u03b2 , increasing expressibility.\n " ,
19- " - **Orbit QAOA** exploits the graph's automorphism group: edges in the same symmetry orbit share a \u03b3 parameter, enabling the optimizer to differentiate structurally distinct edge types without the full multi-angle overhead."
17+ " ### Definitions\n " ,
18+ " \n " ,
19+ " - **Vanilla QAOA** uses a single shared γ for all edges and a single β for all nodes. It is the original QAOA proposal.\n " ,
20+ " \n " ,
21+ " - **Free QAOA** (multi-angle) maximises the number of independent parameters: each edge gets its own γ and each node gets its own β. There is no parameter sharing whatsoever.\n " ,
22+ " \n " ,
23+ " - **Orbit QAOA** (following [arXiv:2410.05187](https://arxiv.org/abs/2410.05187)) exploits the automorphism group Aut(G) of the graph to construct an *equivariant* ansatz. Edges related by a graph automorphism share one γ, and nodes related by a graph automorphism share one β. This is the unique parameterisation that is both equivariant and maximally expressive within the equivariant subspace."
2024 ]
2125 },
2226 {
4953 "source" : [
5054 " ### House Graph\n " ,
5155 " \n " ,
52- " The house graph has 5 nodes and 6 edges arranged as a square with a triangular roof."
56+ " The * house graph* has 5 nodes and 6 edges: a square base with a triangular roof."
5357 ]
5458 },
5559 {
7478 "cell_type" : " markdown" ,
7579 "metadata" : {},
7680 "source" : [
77- " ### Compute the Optimal Cut\n " ,
81+ " ### Optimal Max Cut\n " ,
7882 " \n " ,
79- " Brute-force the minimum cost (maximum cut) so we can calculate approximation ratios ."
83+ " Brute-force the minimum QAOA cost (= maximum cut value) for reference ."
8084 ]
8185 },
8286 {
8690 "outputs" : [],
8791 "source" : [
8892 " problem_ref = problems.MaxCut(G)\n " ,
89- " min_cost, max_cost = problem_ref.computeMinMaxCosts()\n " ,
90- " # cost() returns a positive value; the QAOA minimizes its negative\n " ,
91- " mincost = -min_cost # most negative expectation value = best cut\n " ,
93+ " min_cost, _ = problem_ref.computeMinMaxCosts()\n " ,
94+ " # cost() is positive; QAOA minimises its negative\n " ,
95+ " mincost = -min_cost # most negative expectation = best cut\n " ,
9296 " maxcost = 0\n " ,
9397 " \n " ,
9498 " print(f\" Maximum cut value : {-mincost}\" )\n " ,
95- " print(f\" mincost used for approximation ratio: {mincost}\" )"
99+ " print(f\" mincost ( for approximation ratio) : {mincost}\" )"
96100 ]
97101 },
98102 {
99103 "cell_type" : " markdown" ,
100104 "metadata" : {},
101105 "source" : [
102- " ### Edge Orbits of the House Graph \n " ,
106+ " ### Graph Symmetry Analysis \n " ,
103107 " \n " ,
104- " The orbit QAOA assigns one \u03b3 parameter per edge orbit of the graph's automorphism group."
108+ " The orbit QAOA uses the automorphism group Aut(G) to assign shared parameters.\n " ,
109+ " Below we display the **node orbits** (for the β mixer parameters) and **edge orbits** (for the γ problem parameters)."
105110 ]
106111 },
107112 {
111116 "outputs" : [],
112117 "source" : [
113118 " orbit_problem = problems.MaxCutOrbit(G)\n " ,
114- " print(f\" Number of edge orbits: {orbit_problem.get_num_parameters()}\" )\n " ,
115- " for i, orbit in enumerate(orbit_problem.edge_orbits):\n " ,
116- " print(f\" Orbit {i}: {orbit}\" )"
119+ " orbit_mixer = mixers.XOrbit(G)\n " ,
120+ " \n " ,
121+ " print(f\" Node orbits ({len(orbit_mixer.node_orbits)} total):\" )\n " ,
122+ " for i, orb in enumerate(orbit_mixer.node_orbits):\n " ,
123+ " print(f\" β_{i}: nodes {orb}\" )\n " ,
124+ " \n " ,
125+ " print()\n " ,
126+ " print(f\" Edge orbits ({len(orbit_problem.edge_orbits)} total):\" )\n " ,
127+ " for i, orb in enumerate(orbit_problem.edge_orbits):\n " ,
128+ " print(f\" γ_{i}: edges {orb}\" )"
117129 ]
118130 },
119131 {
120132 "cell_type" : " markdown" ,
121133 "metadata" : {},
122134 "source" : [
123- " ### Create QAOA Instances"
135+ " ### Create QAOA Instances\n " ,
136+ " \n " ,
137+ " Three instances with different parameter budgets per layer:"
124138 ]
125139 },
126140 {
129143 "metadata" : {},
130144 "outputs" : [],
131145 "source" : [
132- " # Vanilla QAOA: X mixer, single \u03b3 and \u03b2 per layer\n qaoa_vanilla = QAOA(\n problem=problems.MaxCut(G),\n mixer=mixers.X(),\n initialstate=initialstates.Plus(),\n )\n\n # Free QAOA: XMultiAngle mixer, one \u03b2 per qubit\n qaoa_free = QAOA(\n problem=problems.MaxCut(G),\n mixer=mixers.XMultiAngle(),\n initialstate=initialstates.Plus(),\n )\n\n # Orbit QAOA: X mixer, one \u03b3 per edge orbit of the graph\n qaoa_orbit = QAOA(\n problem=problems.MaxCutOrbit(G),\n mixer=mixers.X(),\n initialstate=initialstates.Plus(),\n )\n\n # Build circuits at depth 1 to inspect parameter counts\n for q in (qaoa_vanilla, qaoa_free, qaoa_orbit):\n q.createParameterizedCircuit(depth=1)\n\n print(f\" Vanilla \u2014 \u03b3 /layer: {qaoa_vanilla.n_gamma}, \u03b2 /layer: {qaoa_vanilla.n_beta}\" )\n print(f\" Free \u2014 \u03b3 /layer: {qaoa_free.n_gamma}, \u03b2 /layer: {qaoa_free.n_beta}\" )\n print(f\" Orbit \u2014 \u03b3 /layer: {qaoa_orbit.n_gamma}, \u03b2 /layer: {qaoa_orbit.n_beta}\" )"
146+ " # Vanilla: one shared γ and one shared β\n " ,
147+ " qaoa_vanilla = QAOA(\n " ,
148+ " problem=problems.MaxCut(G),\n " ,
149+ " mixer=mixers.X(),\n " ,
150+ " initialstate=initialstates.Plus(),\n " ,
151+ " )\n " ,
152+ " \n " ,
153+ " # Free: one γ per edge, one β per node\n " ,
154+ " qaoa_free = QAOA(\n " ,
155+ " problem=problems.MaxCutFree(G),\n " ,
156+ " mixer=mixers.XMultiAngle(),\n " ,
157+ " initialstate=initialstates.Plus(),\n " ,
158+ " )\n " ,
159+ " \n " ,
160+ " # Orbit: one γ per edge orbit, one β per node orbit (arXiv:2410.05187)\n " ,
161+ " qaoa_orbit = QAOA(\n " ,
162+ " problem=problems.MaxCutOrbit(G),\n " ,
163+ " mixer=mixers.XOrbit(G),\n " ,
164+ " initialstate=initialstates.Plus(),\n " ,
165+ " )\n " ,
166+ " \n " ,
167+ " # Build circuits at depth 1 to inspect parameter counts\n " ,
168+ " for q in (qaoa_vanilla, qaoa_free, qaoa_orbit):\n " ,
169+ " q.createParameterizedCircuit(depth=1)\n " ,
170+ " \n " ,
171+ " print(f\" Vanilla — γ/layer: {qaoa_vanilla.n_gamma}, β/layer: {qaoa_vanilla.n_beta}\" )\n " ,
172+ " print(f\" Free — γ/layer: {qaoa_free.n_gamma} (1 per edge), β/layer: {qaoa_free.n_beta} (1 per node)\" )\n " ,
173+ " print(f\" Orbit — γ/layer: {qaoa_orbit.n_gamma} (1 per edge orbit), β/layer: {qaoa_orbit.n_beta} (1 per node orbit)\" )"
133174 ]
134175 },
135176 {
138179 "source" : [
139180 " ### Energy Landscape at Depth 1 (Vanilla)\n " ,
140181 " \n " ,
141- " Sample the energy landscape over (\u03b3 , \u03b2 ) for the vanilla instance."
182+ " Sample the cost landscape over the (γ, β) grid for the vanilla instance. \n " ,
183+ " This grid search also provides the starting point for deeper optimisation."
142184 ]
143185 },
144186 {
152194 " \n " ,
153195 " fig = plt.figure(figsize=(6, 5))\n " ,
154196 " plot_E(qaoa_vanilla, fig=fig)\n " ,
155- " plt.title(\" Vanilla QAOA \u2014 energy landscape (depth 1)\" )\n " ,
197+ " plt.title(\" Vanilla QAOA — energy landscape (depth 1)\" )\n " ,
156198 " plt.show()"
157199 ]
158200 },
159201 {
160202 "cell_type" : " markdown" ,
161203 "metadata" : {},
162204 "source" : [
163- " ### Run Optimization"
205+ " ### Run Optimisation\n " ,
206+ " \n " ,
207+ " Optimise all three ansätze up to `maxdepth = 5`. \n " ,
208+ " Each depth is initialised using the interpolation heuristic from the previous depth."
164209 ]
165210 },
166211 {
194239 " plot_ApproximationRatio(\n " ,
195240 " qaoa_vanilla, maxdepth,\n " ,
196241 " mincost=mincost, maxcost=maxcost,\n " ,
197- " label=\" Vanilla (X mixer )\" ,\n " ,
242+ " label=f \" Vanilla (1γ + 1β )\" ,\n " ,
198243 " style=\" o--b\" ,\n " ,
199244 " fig=fig,\n " ,
200245 " )\n " ,
201246 " plot_ApproximationRatio(\n " ,
202247 " qaoa_free, maxdepth,\n " ,
203248 " mincost=mincost, maxcost=maxcost,\n " ,
204- " label=\" Free (XMultiAngle mixer )\" ,\n " ,
249+ " label=f \" Free ({qaoa_free.n_gamma}γ + {qaoa_free.n_beta}β, one per edge/node )\" ,\n " ,
205250 " style=\" s--r\" ,\n " ,
206251 " fig=fig,\n " ,
207252 " )\n " ,
208253 " plot_ApproximationRatio(\n " ,
209254 " qaoa_orbit, maxdepth,\n " ,
210255 " mincost=mincost, maxcost=maxcost,\n " ,
211- " label=\" Orbit (MaxCutOrbit + X mixer )\" ,\n " ,
256+ " label=f \" Orbit ({qaoa_orbit.n_gamma}γ + {qaoa_orbit.n_beta}β, one per orbit )\" ,\n " ,
212257 " style=\" ^--g\" ,\n " ,
213258 " fig=fig,\n " ,
214259 " )\n " ,
215260 " \n " ,
216- " plt.title(\" Approximation ratio vs. depth \u2014 House graph\" )\n " ,
261+ " plt.title(\" Approximation ratio vs. depth — House graph\" )\n " ,
217262 " plt.tight_layout()\n " ,
218263 " plt.show()"
219264 ]
224269 "source" : [
225270 " ### Optimal Angles at Maximum Depth (Vanilla)\n " ,
226271 " \n " ,
227- " `plot_angles` expects the standard 1 \u03b3 + 1 \u03b2 per-layer format. \n " ,
228- " We display it for the vanilla variant; the free and orbit instances use a\n " ,
229- " different layout (multiple \u03b3 or \u03b2 per layer)."
272+ " `plot_angles` expects the 1 γ + 1 β per-layer format, so we display it only for the vanilla ansatz."
230273 ]
231274 },
232275 {
237280 "source" : [
238281 " fig = plt.figure()\n " ,
239282 " plot_angles(qaoa_vanilla, maxdepth, label=\" Vanilla\" , style=\" ob\" , fig=fig)\n " ,
240- " plt.title(f\" Optimal angles \u2014 Vanilla QAOA, depth {maxdepth}\" )\n " ,
283+ " plt.title(f\" Optimal angles — Vanilla QAOA, depth {maxdepth}\" )\n " ,
241284 " plt.tight_layout()\n " ,
242285 " plt.show()"
243286 ]
256299 },
257300 "nbformat" : 4 ,
258301 "nbformat_minor" : 4
259- }
302+ }
0 commit comments