diff --git a/team_solutions/pineapple/Final.py b/team_solutions/pineapple/Final.py new file mode 100644 index 0000000..674d5f5 --- /dev/null +++ b/team_solutions/pineapple/Final.py @@ -0,0 +1,72 @@ +from matplotlib import pyplot as plt +from pytket.extensions.qiskit import AerBackend +from pytket.backends.backend import Backend +from pytket.backends.backendresult import BackendResult +from pytket.passes import DecomposeBoxes +from pytket.utils import gen_term_sequence_circuit +import numpy as np +from pytket import Qubit, Circuit +from pytket.pauli import QubitPauliString, Pauli +from pytket.utils import QubitPauliOperator +from typing import List, Tuple, Callable +import networkx as nx +import networkx.algorithms.isomorphism.vf2userfunc as vf2 +import networkx as nx +import numpy as np +import matplotlib.pyplot as plt +import networkx.algorithms.isomorphism.vf2userfunc as vf2 +import os +import pickle + + +def subgraphInduce(G, edge, depth, rename=True): + # return: subgraph induced by edge + current = set(edge) + edges = set([edge]) + for i in range(depth): + next = set() + for node in current: + next.update(G.neighbors(node)) + edges.update(G.edges(node)) + current.update(next) + if rename: + # Rename nodes to 0, 1, 2, ... such that 0 and 1 are the central edge. + current.remove(edge[0]) + current.remove(edge[1]) + current = [edge[0], edge[1]] + list(current) + + edges = [(current.index(e[0]), current.index(e[1])) + for e in edges] + return nx.Graph(list(edges)) + + +x = pickle.load(open("subgraphs3.pkl", "rb")) # Subgraphs with frequency + +our_subgraphs = pickle.load(open("subgraphs.pkl", "rb")) # Subgraphs + +print(len(our_subgraphs)) +freq = [] +for idx, subgraph in enumerate(our_subgraphs): + found = False + for i in x: + if nx.is_isomorphic(i[0], nx.Graph(subgraph)): + found = True + freq.append([idx, i[1]]) + break + if not found: + freq.append([idx, 0]) + + +def filename(graph): + h = "".join(str(i[0]) + str(i[1]) for i in graph) + return h + + +print(len(freq)) +freq = sorted(freq, key=lambda x: x[1], reverse=True) +print("\n".join([str(i) for i in freq])) + +# Figure out which ones we urgently need to find +for i, f in freq: + if not os.path.exists(str(filename(our_subgraphs[i])) + ".pkl"): + print("FUCK", i, f) diff --git a/team_solutions/pineapple/README.md b/team_solutions/pineapple/README.md new file mode 100644 index 0000000..7700fcc --- /dev/null +++ b/team_solutions/pineapple/README.md @@ -0,0 +1,21 @@ +## Challenges on QAOA + +1. This QAOA [(Quantum Approximate Optimisation algorithm)](https://arxiv.org/abs/1411.4028) implementaion uses the most naive possible classical optimisation strategy. Parameters are sampled from a uniform distribution and if a list of parameters increases the value of the cost function these values are stored as the best guess so far. Can you improve on this using a more sophisticated optimisation strategy? COBAYLA and SPSA are two possible methods. + +2. The maxcut problem is one very common application of QAOA. Can you create an implementation of QAOA applied to a different problem? Examples of such problems included 3SAT and the maximum clique problem. Perhaps try and create and implementation which works for a more general Hamiltonian that could contain non-commuting Pauli terms like those found in Quantum Chemistry. Think about what additonal complexity would be added by a Hamiltonian with non-commuting terms. Interesting Hamiltonians to consider could be the Transverse Field Ising Model (TFIM), diatomic Hydrogen or a simple compound like lithium hydride. + +3. The given code implements QAOA on the idealised AerBackend simulator. Try instead to use a device/emulator with noise (i.e. the H1-2 emulator with the pytket-quantinuum extension). Can you optimise your circuit with pytket passes to improve performance in the presence of noise? + +4. Currently the circuits have to be recompiled on every iteration leading to a non-trivial compilation overhead if we use a large number of iterations. Can you think of a way to improve this? + +5. Implement a qubit reuse strategy to allow for the execution of a large QAOA instance on a small quantum device/emulator. See [this paper](https://arxiv.org/abs/2210.08039) for ideas. + +## Resources + +1. QAOA original paper (Farhi et al) -> https://arxiv.org/abs/1411.4028 +2. Quantinuum Qubit reuse paper (DeCross et al) -> https://arxiv.org/abs/2210.08039 +3. pytket API documentation -> https://cqcl.github.io/tket/pytket/api/ +4. User manual -> https://cqcl.github.io/pytket/manual/index.html +5. Notebook examples -> https://github.com/CQCL/pytket/tree/main/examples +6. Qiskit textbook section on QAOA -> https://qiskit.org/textbook/ch-applications/qaoa.html +7. Recent QAOA/qiskit review -> https://arxiv.org/abs/2301.09535 diff --git a/team_solutions/pineapple/Subgraph.py b/team_solutions/pineapple/Subgraph.py new file mode 100644 index 0000000..0a94365 --- /dev/null +++ b/team_solutions/pineapple/Subgraph.py @@ -0,0 +1,75 @@ +import networkx as nx +import numpy as np +import matplotlib.pyplot as plt +import networkx.algorithms.isomorphism.vf2userfunc as vf2 + + +def subgraphInduce(G, edge, depth, rename=False): + # return: subgraph induced by edge + current = set([edge[0], edge[1]]) + edges = set([edge]) + for i in range(depth): + next = set() + for node in current: + next.update(G.neighbors(node)) + edges.update(G.edges(node)) + current.update(next) + if rename: + current = {x: i for i, x in enumerate(list(current))} + edges = [(current[edge[0]], current[edge[1]]) for edge in edges] + return nx.Graph(list(edges)) + + +# adj = list(G.neighbors(0)) +# print(adj[0]) +# print(subgraphInduce(G, (0, adj[0]), 1).edges()) +# print(subgraphInduce(G, (0, adj[0]), 1, True).edges()) + +''' +for i in range(40): + for t in range(20, 30, 2): + G = nx.random_regular_graph(3, t) + for edge in G.edges(): + subgraph = subgraphInduce(G, edge, 2) + seen = False + # for k, (i, j) in enumerate(res): + # if nx.is_isomorphic(subgraph, i): + # seen = True + # res[k][1] += 1 + # break + for i in res: + if nx.is_isomorphic(subgraph, i): + seen = True + break + if not seen: + #print(len(res)) + #res.append([subgraph, 1]) + res.append(subgraph)''' + + +def gimme_subgraphs(): + res = [] + + for t in range(20, 100, 2): + G = nx.random_regular_graph(3, t) + for edge in G.edges(): + subgraph = subgraphInduce(G, edge, 2) + seen = False + for i in res: + if nx.is_isomorphic(subgraph, i): + seen = True + # res[i][1] += 1 + break + if not seen: + # print(len(res)) + # res.append([subgraph, 1]) + res.append(subgraph) + print("Generated Subgraphs len = ", len(res)) + return res + + +# res = sorted(res, key=lambda x: x[1], reverse=True) +# for i in range(100): +# print(res[i][1]) +# nx.draw(G) +# plt.show() diff --git a/team_solutions/pineapple/Team_Pineapple.pdf b/team_solutions/pineapple/Team_Pineapple.pdf new file mode 100644 index 0000000..edc2d6b Binary files /dev/null and b/team_solutions/pineapple/Team_Pineapple.pdf differ diff --git a/team_solutions/pineapple/four_color.ipynb b/team_solutions/pineapple/four_color.ipynb new file mode 100644 index 0000000..6185e82 --- /dev/null +++ b/team_solutions/pineapple/four_color.ipynb @@ -0,0 +1,872 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3b602e8-f1ca-4f06-9079-5000fbbaf8f8", + "metadata": {}, + "source": [ + "# Four Coloring Problem" + ] + }, + { + "cell_type": "markdown", + "id": "1a93c695-d39f-4651-82fe-4b87bc3e2156", + "metadata": {}, + "source": [ + "## Graph Construction" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f1a871da-89ec-4ff9-a074-5b5a40627a27", + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "from collections import defaultdict" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "4e4449ae-93f9-426f-a575-c72167a65479", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# bigger graph\n", + "graph_edges = [(0, 1), (0,2), (1,2), (1,3), (3,4), (0, 4), (1, 4), (0, 3), (2, 4)]\n", + "N = 5\n", + "G = nx.Graph()\n", + "G.add_edges_from(graph_edges)\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, labels={node: node for node in G.nodes()})\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "4f6d078a-3c95-40ff-86cf-fdd08f7dd722", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "graph_edges = [(0, 1), (0,2), (1,2)]\n", + "N = 3\n", + "G = nx.Graph()\n", + "G.add_edges_from(graph_edges)\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, labels={node: node for node in G.nodes()})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "682e7535-41c4-4519-966e-c35de1e05cdb", + "metadata": {}, + "source": [ + "## Cost Hamiltonian" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "f010b143-c398-458f-9c1a-6473de5ca3d4", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple, Any, Dict\n", + "from pytket.utils import QubitPauliOperator\n", + "from pytket.pauli import QubitPauliString, Pauli\n", + "from pytket import Qubit" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "efd6691f-f389-4c4b-9d1d-55294a4f4b8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(): 2.25000000000000, (Zq[0], Zq[1]): -0.250000000000000, (Zq[3], Zq[4]): -0.250000000000000, (Zq[0], Zq[1], Zq[3], Zq[4]): -0.250000000000000, (Zq[0], Zq[2]): -0.250000000000000, (Zq[3], Zq[5]): -0.250000000000000, (Zq[0], Zq[2], Zq[3], Zq[5]): -0.250000000000000, (Zq[1], Zq[2]): -0.250000000000000, (Zq[4], Zq[5]): -0.250000000000000, (Zq[1], Zq[2], Zq[4], Zq[5]): -0.250000000000000}\n" + ] + } + ], + "source": [ + "def qaoa_graph_to_cost_hamiltonian(edges, cost_angle: float) -> QubitPauliOperator:\n", + " qpo_dict = defaultdict(lambda:0)\n", + " for (i, j) in edges:\n", + " qpo_dict[QubitPauliString()]+=3/4\n", + " qpo_dict[QubitPauliString([Qubit(i), Qubit(j)], [Pauli.Z]*2)]-=1/4\n", + " qpo_dict[QubitPauliString([Qubit(N+i), Qubit(N+j)], [Pauli.Z]*2)]-=1/4\n", + " qpo_dict[QubitPauliString([Qubit(i), Qubit(j), Qubit(N+i), Qubit(N+j)], [Pauli.Z]*4)]-=1/4\n", + " for key in qpo_dict.keys():\n", + " qpo_dict[key]*=cost_angle\n", + " return QubitPauliOperator(qpo_dict)\n", + "\n", + "cost_angle = 1.0\n", + "cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(graph_edges, cost_angle)\n", + "print(cost_ham_qpo)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "a42b416d-97f4-4d66-8651-1f9ab0b1444a", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket import Circuit\n", + "\n", + "circ = Circuit(10)\n", + "# circ.X(0)\n", + "# circ.X(N+1)\n", + "# circ.X(N+4)\n", + "# circ.X(4)\n", + "# circ.X(7)\n", + "vec = circ.get_statevector()" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "2c7dd7c0-fdd4-4443-b257-060891eeb4c1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0j" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cost_ham_qpo.state_expectation(vec)" + ] + }, + { + "cell_type": "markdown", + "id": "dd0775f2-e48d-4378-a130-34f1b6653181", + "metadata": {}, + "source": [ + "## Cost Hamiltonian Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "2d7f7edc-3934-441b-9417-fcbc7dda5efc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.utils import gen_term_sequence_circuit\n", + "from pytket import Circuit\n", + "from pytket.circuit import display\n", + "\n", + "cost_ham_circuit = gen_term_sequence_circuit(cost_ham_qpo, Circuit(N*2))\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "715d357e-da28-468a-aa24-b9385f9e8009", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.transform import Transform\n", + "\n", + "Transform.DecomposeBoxes().apply(cost_ham_circuit)\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "43b9166e-a355-488b-98d3-be988f52fe98", + "metadata": {}, + "source": [ + "## Mixer Hamiltonian" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "1f82803b-15c2-4486-8bf6-461c59dd535f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mixer_angle = 0.8\n", + "mixer_ham_qpo = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer_angle for i in range(N*2)})\n", + "mixer_ham_circuit = gen_term_sequence_circuit(mixer_ham_qpo, Circuit(N*2))\n", + "Transform.DecomposeBoxes().apply(mixer_ham_circuit)\n", + "display.render_circuit_jupyter(mixer_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "3d298d00-124f-46de-b2c0-d58dbf9dd937", + "metadata": {}, + "source": [ + "## Initial Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "51d87382-6263-49c7-b0d6-f74cf4c9077c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def qaoa_initial_circuit(n_qubits: int) -> Circuit:\n", + " c = Circuit(n_qubits)\n", + " for i in range(n_qubits):\n", + " c.H(i)\n", + " return c\n", + "\n", + "superposition_circuit = qaoa_initial_circuit(N)\n", + "\n", + "display.render_circuit_jupyter(superposition_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "ddb7438f-34a5-4d7a-815b-283252d3f7f2", + "metadata": {}, + "source": [ + "## Putting it all together" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "ab0e4675-f644-4227-82ce-f2bd3ea592ad", + "metadata": {}, + "outputs": [], + "source": [ + "def qaoa_circuit(edges,\n", + " n_nodes: int,\n", + " mixer_angles: List[float],\n", + " cost_angles: List[float]) -> Circuit:\n", + " \n", + " assert len(mixer_angles) == len(cost_angles)\n", + " \n", + " # initial state\n", + " qaoa_circuit = qaoa_initial_circuit(n_nodes*2)\n", + " \n", + " # add cost and mixer terms to state\n", + " for cost, mixer in zip(cost_angles, mixer_angles):\n", + " cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost)\n", + " mixer_ham = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer for i in range(n_nodes*2)})\n", + " qaoa_circuit.append(gen_term_sequence_circuit(cost_ham, Circuit(n_nodes*2)))\n", + " qaoa_circuit.append(gen_term_sequence_circuit(mixer_ham, Circuit(n_nodes*2)))\n", + " \n", + " Transform.DecomposeBoxes().apply(qaoa_circuit)\n", + " return qaoa_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "58f78361-305b-41ef-9aa7-08a4b6a17b47", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple\n", + "from pytket.backends.backendresult import BackendResult\n", + "\n", + "def edges_fail(edges, meas):\n", + " num = 0\n", + " for (i, j) in edges:\n", + " num+=(meas[i] == meas[j] and meas[N+i] == meas[N+j])\n", + " return num\n", + "\n", + "def get_energy(edges, results: BackendResult) -> float:\n", + " dist = results.get_distribution()\n", + " energy = 0\n", + " for meas, prob in dist.items():\n", + " energy += (len(edges) - edges_fail(edges, meas))*prob\n", + "\n", + " return energy" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "a183907e-714e-4a5f-be72-4610a33ea122", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket.backends.backend import Backend\n", + "from typing import Callable\n", + "import numpy as np\n", + "\n", + "def qaoa_instance(\n", + " backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " guess_mixer_angles: np.array,\n", + " guess_cost_angles: np.array,\n", + " seed: int,\n", + " shots: int = 5000,\n", + ") -> float:\n", + " # step 1: get state guess\n", + " my_prep_circuit = qaoa_circuit(\n", + " graph_edges, N, guess_mixer_angles, guess_cost_angles\n", + " )\n", + " measured_circ = my_prep_circuit.copy().measure_all()\n", + " compiler_pass(measured_circ)\n", + " res = backend.run_circuit(measured_circ, shots, seed=seed)\n", + "\n", + " return get_energy(graph_edges, res)" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "f7bc1fa6-0232-41ed-bba5-c896186c3d3b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0.68100884, -1.30967135])" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.random.normal([0.5, 0.6], [1, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "id": "f562e4e6-2b7f-4d0e-880c-613f5c20eb85", + "metadata": {}, + "outputs": [], + "source": [ + "def qaoa_optimise_energy(compiler_pass: Callable[[Circuit], bool],\n", + " backend: Backend,\n", + " iterations: int = 100,\n", + " n: int = 3,\n", + " shots: int = 5000,\n", + " seed: int= 12345):\n", + " \n", + " highest_energy = -1000 \n", + " best_guess_mixer_angles = [0 for i in range(n)] \n", + " best_guess_cost_angles = [0 for i in range(n)]\n", + " rng = np.random.default_rng(seed)\n", + " # guess some angles (iterations)-times and try if they are better than the best angles found before\n", + " \n", + " for i in range(iterations):\n", + " \n", + " guess_mixer_angles = np.clip(np.random.normal(best_guess_mixer_angles, [1]*n), 0, 1)\n", + " guess_cost_angles = np.clip(np.random.normal(best_guess_cost_angles, [1]*n), 0, 1)\n", + " \n", + " qaoa_energy = qaoa_instance(backend,\n", + " compiler_pass,\n", + " guess_mixer_angles,\n", + " guess_cost_angles,\n", + " seed=seed,\n", + " shots=shots)\n", + " \n", + " if(qaoa_energy > highest_energy):\n", + " \n", + " print(\"new highest energy found: \", qaoa_energy)\n", + " \n", + " best_guess_mixer_angles = np.round(guess_mixer_angles, 3)\n", + " best_guess_cost_angles = np.round(guess_cost_angles, 3)\n", + " highest_energy = qaoa_energy\n", + " \n", + " print(\"highest energy: \", highest_energy)\n", + " print(\"best guess mixer angles: \", best_guess_mixer_angles)\n", + " print(\"best guess cost angles: \", best_guess_cost_angles)\n", + " return best_guess_mixer_angles, best_guess_cost_angles" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "id": "2e8941c3-b604-45c6-8aba-de496cca9a80", + "metadata": {}, + "outputs": [], + "source": [ + "def qaoa_calculate(backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " shots: int = 5000,\n", + " iterations: int = 100,\n", + " seed: int = 12345,\n", + " ) -> BackendResult:\n", + " \n", + " # find the parameters for the highest energy\n", + " best_mixer, best_cost = qaoa_optimise_energy(compiler_pass,\n", + " backend,\n", + " iterations,\n", + " 10,\n", + " shots=shots,\n", + " seed=seed)\n", + " \n", + " # get the circuit with the final parameters of the optimisation:\n", + " my_qaoa_circuit = qaoa_circuit(graph_edges, N,\n", + " best_mixer,\n", + " best_cost)\n", + "\n", + " my_qaoa_circuit.measure_all()\n", + "\n", + " compiler_pass(my_qaoa_circuit)\n", + " handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed)\n", + "\n", + " result = backend.get_result(handle) \n", + " \n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "id": "377cf5cf-21cd-492a-8bab-d4a9443aea53", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "id": "1b7112e5-eb3f-4a40-8165-a3fa7f9ce26a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new highest energy found: 1.760000000000001\n", + "new highest energy found: 2.5500000000000007\n", + "new highest energy found: 2.630000000000001\n", + "new highest energy found: 2.71\n", + "new highest energy found: 2.7200000000000006\n", + "highest energy: 2.7200000000000006\n", + "best guess mixer angles: [1. 1. 0. 0. 1. 0. 0. 0. 0.12 1. ]\n", + "best guess cost angles: [1. 0.206 0.99 0. 0.524 0. 0. 0.912 1. 0. ]\n", + "CPU times: user 3min 4s, sys: 87.3 ms, total: 3min 4s\n", + "Wall time: 3min 4s\n" + ] + } + ], + "source": [ + "%%time\n", + "res = qaoa_calculate(backend, backend.default_compilation_pass(2).apply, shots = 100, iterations = 100, seed=43278)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "f002c01a-2224-48e9-b038-cef48dbabadb", + "metadata": {}, + "outputs": [], + "source": [ + "def prob_each_node(dist):\n", + " p = [0]*N*2\n", + " for meas, prob in dist.items():\n", + " for ind, val in enumerate(meas):\n", + " if (val == 1): \n", + " p[ind] +=prob\n", + " return p" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "id": "448563e0-b558-472d-abb9-772c2552c314", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{(0, 0, 0, 0, 0, 0): 0.01,\n", + " (0, 0, 0, 1, 1, 1): 0.01,\n", + " (0, 0, 1, 0, 1, 0): 0.05,\n", + " (0, 0, 1, 0, 1, 1): 0.03,\n", + " (0, 0, 1, 1, 0, 0): 0.03,\n", + " (0, 0, 1, 1, 0, 1): 0.04,\n", + " (0, 0, 1, 1, 1, 0): 0.01,\n", + " (0, 1, 0, 0, 0, 1): 0.04,\n", + " (0, 1, 0, 0, 1, 1): 0.04,\n", + " (0, 1, 0, 1, 0, 0): 0.07,\n", + " (0, 1, 0, 1, 1, 0): 0.02,\n", + " (0, 1, 1, 0, 0, 1): 0.06,\n", + " (0, 1, 1, 0, 1, 0): 0.05,\n", + " (0, 1, 1, 1, 0, 0): 0.01,\n", + " (0, 1, 1, 1, 0, 1): 0.03,\n", + " (0, 1, 1, 1, 1, 0): 0.05,\n", + " (1, 0, 0, 0, 0, 1): 0.01,\n", + " (1, 0, 0, 0, 1, 0): 0.05,\n", + " (1, 0, 0, 0, 1, 1): 0.02,\n", + " (1, 0, 0, 1, 0, 1): 0.05,\n", + " (1, 0, 0, 1, 1, 0): 0.03,\n", + " (1, 0, 1, 0, 0, 1): 0.04,\n", + " (1, 0, 1, 0, 1, 0): 0.01,\n", + " (1, 0, 1, 0, 1, 1): 0.05,\n", + " (1, 0, 1, 1, 0, 0): 0.02,\n", + " (1, 0, 1, 1, 1, 0): 0.03,\n", + " (1, 1, 0, 0, 1, 0): 0.02,\n", + " (1, 1, 0, 0, 1, 1): 0.03,\n", + " (1, 1, 0, 1, 0, 0): 0.05,\n", + " (1, 1, 0, 1, 0, 1): 0.04}" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.get_distribution()" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "373de61a-7196-4437-8ea3-d3a57bffa2f0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.4200000000000002,\n", + " 0.5700000000000003,\n", + " 0.4300000000000002,\n", + " 0.47000000000000025,\n", + " 0.5400000000000003,\n", + " 0.5000000000000002,\n", + " 0.5700000000000003,\n", + " 0.48000000000000026,\n", + " 0.4800000000000003,\n", + " 0.49000000000000027]" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "prob_each_node(res.get_distribution())" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "id": "fd60112a-7bfe-41e6-b0ca-a810331c4150", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.32600000000000007" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_energy(adj, res)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba155c72-d9ba-42c9-8f13-3a7ce445199a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/team_solutions/pineapple/fourier.py b/team_solutions/pineapple/fourier.py new file mode 100644 index 0000000..e69de29 diff --git a/team_solutions/pineapple/interpolate_maxcut.py b/team_solutions/pineapple/interpolate_maxcut.py new file mode 100644 index 0000000..0dd031c --- /dev/null +++ b/team_solutions/pineapple/interpolate_maxcut.py @@ -0,0 +1,202 @@ +from maxcut_plotting import plot_maxcut_results +from pytket.extensions.qiskit import AerBackend +from pytket.backends.backend import Backend +from pytket.backends.backendresult import BackendResult +from pytket.passes import DecomposeBoxes +from pytket.utils import gen_term_sequence_circuit +import numpy as np +from pytket import Qubit, Circuit +from pytket.pauli import QubitPauliString, Pauli +from pytket.utils import QubitPauliOperator +from typing import List, Tuple, Callable +import networkx as nx + +# Define graph. + +max_cut_graph_edges = [(0, 1), (1, 2), (1, 3), (3, 4), (4, 5), (4, 6)] +n_nodes = 7 + +max_cut_graph = nx.Graph() +max_cut_graph.add_edges_from(max_cut_graph_edges) +nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()}) + +expected_results = [(0, 1, 0, 0, 1, 0, 0), (1, 0, 1, 1, 0, 1, 1)] + + +def qaoa_graph_to_cost_hamiltonian( + edges: List[Tuple[int, int]], cost_angle: float +) -> QubitPauliOperator: + """ + This function takes a list of edges and a cost angle and returns a QubitPauliOperator + representing the cost Hamiltonian for the QAOA algorithm. + + """ + qpo_dict = {QubitPauliString(): len(edges) * 0.5 * cost_angle} + for e in edges: + term_string = QubitPauliString( + [Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z]) + qpo_dict[term_string] = -0.5 * cost_angle + return QubitPauliOperator(qpo_dict) + + +cost_angle = 1.0 +cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle) +print(cost_ham_qpo) + + +def qaoa_initial_circuit(n_qubits: int) -> Circuit: + c = Circuit(n_qubits) + for i in range(n_qubits): + c.H(i) + return c + + +def qaoa_max_cut_circuit( + edges: List[Tuple[int, int]], + n_nodes: int, + mixer_angles: List[float], + cost_angles: List[float], +) -> Circuit: + """ + Create a QAOA circuit for the MaxCut problem. + """ + + assert len(mixer_angles) == len(cost_angles) + + # initial state + qaoa_circuit = qaoa_initial_circuit(n_nodes) + + # add cost and mixer terms to state + for cost, mixer in zip(cost_angles, mixer_angles): + cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost) + mixer_ham = QubitPauliOperator( + {QubitPauliString([Qubit(i)], [Pauli.X]) + : mixer for i in range(n_nodes)} + ) + qaoa_circuit.append(gen_term_sequence_circuit( + cost_ham, Circuit(n_nodes))) + qaoa_circuit.append(gen_term_sequence_circuit( + mixer_ham, Circuit(n_nodes))) + + DecomposeBoxes().apply(qaoa_circuit) + return qaoa_circuit + + +def max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult, maximize=False) -> float: + energy = 0.0 + dist = results.get_distribution() + if maximize: + for meas in dist.keys(): + energy = max(energy, sum((meas[i] ^ meas[j]) for i, j in edges)) + else: + for i, j in edges: + energy += sum((meas[i] ^ meas[j]) * + prob for meas, prob in dist.items()) + + return energy + + +def qaoa_instance_simple( + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + guess_mixer_angles: np.array, + guess_cost_angles: np.array, + seed: int, + shots: int = 5000, +) -> float: + # step 1: get state guess + my_prep_circuit = qaoa_max_cut_circuit( + max_cut_graph_edges, n_nodes, guess_mixer_angles, guess_cost_angles + ) + measured_circ = my_prep_circuit.copy().measure_all() + compiler_pass(measured_circ) + res = backend.run_circuit(measured_circ, shots, seed=seed) + + return max_cut_energy(max_cut_graph_edges, res) + + +def qaoa_optimise_energy( + compiler_pass: Callable[[Circuit], bool], + backend: Backend, + iterations: int = 100, + n: int = 3, + shots: int = 5000, + seed: int = 12345, +): + + highest_energy = 0 + best_guess_mixer_angles = [0 for i in range(n)] + best_guess_cost_angles = [0 for i in range(n)] + rng = np.random.default_rng(seed) + # guess some angles (iterations)-times and try if they are better than the best angles found before + + for i in range(iterations): + + guess_mixer_angles = rng.uniform(0, 1, n) + guess_cost_angles = rng.uniform(0, 1, n) + + qaoa_energy = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles, + guess_cost_angles, + seed=seed, + shots=shots, + ) + + if qaoa_energy > highest_energy: + + print("new highest energy found: ", qaoa_energy) + + best_guess_mixer_angles = guess_mixer_angles + best_guess_cost_angles = guess_cost_angles + highest_energy = qaoa_energy + + print("highest energy: ", highest_energy) + print("best guess mixer angles: ", best_guess_mixer_angles) + print("best guess cost angles: ", best_guess_cost_angles) + return best_guess_mixer_angles, best_guess_cost_angles + + +def qaoa_calculate( + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + shots: int = 5000, + iterations: int = 100, + seed: int = 12345, +) -> float: + + # find the parameters for the highest energy + best_mixer, best_cost = qaoa_optimise_energy( + compiler_pass, backend, iterations, 3, shots=shots, seed=seed + ) + + # get the circuit with the final parameters of the optimisation: + my_qaoa_circuit = qaoa_max_cut_circuit( + max_cut_graph_edges, n_nodes, best_mixer, best_cost + ) + + my_qaoa_circuit.measure_all() + + compiler_pass(my_qaoa_circuit) + handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed) + + result = backend.get_result(handle) + + return result + + +backend = AerBackend() +comp = backend.get_compiled_circuit + +res = qaoa_calculate( + backend, + backend.default_compilation_pass(2).apply, + shots=5000, + iterations=100, + seed=12345, + maximize=True +) + + +plot_maxcut_results(res, 6) diff --git a/team_solutions/pineapple/maxcut.py b/team_solutions/pineapple/maxcut.py new file mode 100644 index 0000000..b6e0f6c --- /dev/null +++ b/team_solutions/pineapple/maxcut.py @@ -0,0 +1,190 @@ +import networkx as nx + +max_cut_graph_edges = [(0, 1), (1, 2), (1, 3), (3, 4), (4, 5), (4, 6)] +n_nodes = 7 + +max_cut_graph = nx.Graph() +max_cut_graph.add_edges_from(max_cut_graph_edges) +nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()}) + +expected_results = [(0, 1, 0, 0, 1, 0, 0), (1, 0, 1, 1, 0, 1, 1)] + +from typing import List, Tuple, Callable +from pytket.utils import QubitPauliOperator +from pytket.pauli import QubitPauliString, Pauli +from pytket import Qubit, Circuit +import numpy as np + + +def qaoa_graph_to_cost_hamiltonian( + edges: List[Tuple[int, int]], cost_angle: float +) -> QubitPauliOperator: + qpo_dict = {QubitPauliString(): len(edges) * 0.5 * cost_angle} + for e in edges: + term_string = QubitPauliString([Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z]) + qpo_dict[term_string] = -0.5 * cost_angle + return QubitPauliOperator(qpo_dict) + + +cost_angle = 1.0 +cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle) +print(cost_ham_qpo) + + +def qaoa_initial_circuit(n_qubits: int) -> Circuit: + c = Circuit(n_qubits) + for i in range(n_qubits): + c.H(i) + return c + + +from pytket.utils import gen_term_sequence_circuit +from pytket.passes import DecomposeBoxes + + +def qaoa_max_cut_circuit( + edges: List[Tuple[int, int]], + n_nodes: int, + mixer_angles: List[float], + cost_angles: List[float], +) -> Circuit: + + assert len(mixer_angles) == len(cost_angles) + + # initial state + qaoa_circuit = qaoa_initial_circuit(n_nodes) + + # add cost and mixer terms to state + for cost, mixer in zip(cost_angles, mixer_angles): + cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost) + mixer_ham = QubitPauliOperator( + {QubitPauliString([Qubit(i)], [Pauli.X]): mixer for i in range(n_nodes)} + ) + qaoa_circuit.append(gen_term_sequence_circuit(cost_ham, Circuit(n_nodes))) + qaoa_circuit.append(gen_term_sequence_circuit(mixer_ham, Circuit(n_nodes))) + + DecomposeBoxes().apply(qaoa_circuit) + return qaoa_circuit + + +from pytket.backends.backendresult import BackendResult + + +def max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult) -> float: + energy = 0.0 + dist = results.get_distribution() + for i, j in edges: + energy += sum((meas[i] ^ meas[j]) * prob for meas, prob in dist.items()) + + return energy + + +from pytket.backends.backend import Backend + + +def qaoa_instance_simple( + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + guess_mixer_angles: np.array, + guess_cost_angles: np.array, + seed: int, + shots: int = 5000, +) -> float: + # step 1: get state guess + my_prep_circuit = qaoa_max_cut_circuit( + max_cut_graph_edges, n_nodes, guess_mixer_angles, guess_cost_angles + ) + measured_circ = my_prep_circuit.copy().measure_all() + compiler_pass(measured_circ) + res = backend.run_circuit(measured_circ, shots, seed=seed) + + return max_cut_energy(max_cut_graph_edges, res) + + +def qaoa_optimise_energy( + compiler_pass: Callable[[Circuit], bool], + backend: Backend, + iterations: int = 100, + n: int = 3, + shots: int = 5000, + seed: int = 12345, +): + + highest_energy = 0 + best_guess_mixer_angles = [0 for i in range(n)] + best_guess_cost_angles = [0 for i in range(n)] + rng = np.random.default_rng(seed) + # guess some angles (iterations)-times and try if they are better than the best angles found before + + for i in range(iterations): + + guess_mixer_angles = rng.uniform(0, 1, n) + guess_cost_angles = rng.uniform(0, 1, n) + + qaoa_energy = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles, + guess_cost_angles, + seed=seed, + shots=shots, + ) + + if qaoa_energy > highest_energy: + + print("new highest energy found: ", qaoa_energy) + + best_guess_mixer_angles = guess_mixer_angles + best_guess_cost_angles = guess_cost_angles + highest_energy = qaoa_energy + + print("highest energy: ", highest_energy) + print("best guess mixer angles: ", best_guess_mixer_angles) + print("best guess cost angles: ", best_guess_cost_angles) + return best_guess_mixer_angles, best_guess_cost_angles + + +def qaoa_calculate( + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + shots: int = 5000, + iterations: int = 100, + seed: int = 12345, +) -> float: + + # find the parameters for the highest energy + best_mixer, best_cost = qaoa_optimise_energy( + compiler_pass, backend, iterations, 3, shots=shots, seed=seed + ) + + # get the circuit with the final parameters of the optimisation: + my_qaoa_circuit = qaoa_max_cut_circuit( + max_cut_graph_edges, n_nodes, best_mixer, best_cost + ) + + my_qaoa_circuit.measure_all() + + compiler_pass(my_qaoa_circuit) + handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed) + + result = backend.get_result(handle) + + return result + + +from pytket.extensions.qiskit import AerBackend + +backend = AerBackend() +comp = backend.get_compiled_circuit + +res = qaoa_calculate( + backend, + backend.default_compilation_pass(2).apply, + shots=5000, + iterations=100, + seed=12345, +) + +from maxcut_plotting import plot_maxcut_results + +plot_maxcut_results(res, 6) diff --git a/team_solutions/pineapple/maxcutSGD.py b/team_solutions/pineapple/maxcutSGD.py new file mode 100644 index 0000000..7570030 --- /dev/null +++ b/team_solutions/pineapple/maxcutSGD.py @@ -0,0 +1,257 @@ +import networkx as nx + +max_cut_graph_edges = [(0, 1), (1, 2), (1, 3), (3, 4), (4, 5), (4, 6)] +n_nodes = 7 + +max_cut_graph = nx.Graph() +max_cut_graph.add_edges_from(max_cut_graph_edges) +nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()}) + +expected_results = [(0, 1, 0, 0, 1, 0, 0), (1, 0, 1, 1, 0, 1, 1)] + +from typing import List, Tuple, Callable +from pytket.utils import QubitPauliOperator +from pytket.pauli import QubitPauliString, Pauli +from pytket import Qubit, Circuit +import numpy as np + + +def qaoa_graph_to_cost_hamiltonian( + edges: List[Tuple[int, int]], cost_angle: float +) -> QubitPauliOperator: + qpo_dict = {QubitPauliString(): len(edges) * 0.5 * cost_angle} + for e in edges: + term_string = QubitPauliString([Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z]) + qpo_dict[term_string] = -0.5 * cost_angle + return QubitPauliOperator(qpo_dict) + + +cost_angle = 1.0 +cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle) +print(cost_ham_qpo) + + +def qaoa_initial_circuit(n_qubits: int) -> Circuit: + c = Circuit(n_qubits) + for i in range(n_qubits): + c.H(i) + return c + + +from pytket.utils import gen_term_sequence_circuit +from pytket.passes import DecomposeBoxes + + +def qaoa_max_cut_circuit( + edges: List[Tuple[int, int]], + n_nodes: int, + mixer_angles: List[float], + cost_angles: List[float], +) -> Circuit: + + assert len(mixer_angles) == len(cost_angles) + + # initial state + qaoa_circuit = qaoa_initial_circuit(n_nodes) + + # add cost and mixer terms to state + for cost, mixer in zip(cost_angles, mixer_angles): + cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost) + mixer_ham = QubitPauliOperator( + {QubitPauliString([Qubit(i)], [Pauli.X]): mixer for i in range(n_nodes)} + ) + qaoa_circuit.append(gen_term_sequence_circuit(cost_ham, Circuit(n_nodes))) + qaoa_circuit.append(gen_term_sequence_circuit(mixer_ham, Circuit(n_nodes))) + + DecomposeBoxes().apply(qaoa_circuit) + return qaoa_circuit + + +from pytket.backends.backendresult import BackendResult + + +def max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult) -> float: + energy = 0.0 + dist = results.get_distribution() + for i, j in edges: + energy += sum((meas[i] ^ meas[j]) * prob for meas, prob in dist.items()) + + return energy + + +from pytket.backends.backend import Backend + + +def qaoa_instance_simple( + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + guess_mixer_angles: np.array, + guess_cost_angles: np.array, + seed: int, + shots: int = 5000, +) -> float: + # step 1: get state guess + my_prep_circuit = qaoa_max_cut_circuit( + max_cut_graph_edges, n_nodes, guess_mixer_angles, guess_cost_angles + ) + measured_circ = my_prep_circuit.copy().measure_all() + compiler_pass(measured_circ) + res = backend.run_circuit(measured_circ, shots, seed=seed) + + return max_cut_energy(max_cut_graph_edges, res) + + +def qaoa_optimise_energy( + compiler_pass: Callable[[Circuit], bool], + backend: Backend, + iterations: int = 1000, + n: int = 3, + shots: int = 5000, + seed: int = 12345, + learning_rate: float = 0.0001, + stepsize: float = 0.0001, +): + + highest_energy = 0 + rng = np.random.default_rng(seed) + guess_mixer_angles = rng.uniform(0, 1, n) + guess_cost_angles = rng.uniform(0, 1, n) + + # guess some angles (iterations)-times and try if they are better than the best angles found before + + for i in range(iterations): + mixer_gradient = np.ones(n) + cost_gradient = np.ones(n) + qaoa_energy = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles, + guess_cost_angles, + seed=seed, + shots=shots, + ) + + mixer_perturbation = 1 #rng.uniform(0.1, 1, n) + cost_perturbation = 1 # rng.uniform(0.1, 1, n) + + qaoa_energy1 = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles + stepsize*mixer_perturbation, + guess_cost_angles, + seed=seed, + shots=shots, + ) + + qaoa_energy2 = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles - stepsize*mixer_perturbation, + guess_cost_angles, + seed=seed, + shots=shots, + ) + + qaoa_energy3 = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles, + guess_cost_angles + stepsize*cost_perturbation, + seed=seed, + shots=shots, + ) + + qaoa_energy4 = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles, + guess_cost_angles - stepsize*cost_perturbation, + seed=seed, + shots=shots, + ) + + mixer_gradient = (qaoa_energy1 - qaoa_energy2)/(2*stepsize*mixer_perturbation) + cost_gradient = (qaoa_energy3 - qaoa_energy4)/(2*stepsize*cost_perturbation) + + new_mixer_angles = guess_mixer_angles + learning_rate*mixer_gradient + new_cost_angles = guess_cost_angles + learning_rate*cost_gradient + + qaoa_newenergy = qaoa_instance_simple( + backend, + compiler_pass, + new_mixer_angles, + new_cost_angles, + seed=seed, + shots=shots, + ) + + + guess_mixer_angles = new_mixer_angles + guess_cost_angles = new_cost_angles + + print("iteration: ", i ) + print("energy: ", qaoa_energy) + print("old parameters: ", guess_mixer_angles - learning_rate*mixer_gradient, guess_cost_angles - learning_rate*cost_gradient) + print("new parameters: ", guess_mixer_angles, guess_cost_angles) + print("new energy: ", qaoa_newenergy) + print("learning rate:", learning_rate) + + qaoa_energy = qaoa_instance_simple( + backend, + compiler_pass, + guess_mixer_angles, + guess_cost_angles, + seed=seed, + shots=shots, + ) + + print("highest energy: ", qaoa_energy) + print("best guess mixer angles: ", guess_mixer_angles) + print("best guess cost angles: ", guess_cost_angles) + return guess_mixer_angles, guess_cost_angles + + +def qaoa_calculate( + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + shots: int = 5000, + iterations: int = 1000, + seed: int = 12345, +) -> float: + + # find the parameters for the highest energy + best_mixer, best_cost = qaoa_optimise_energy( + compiler_pass, backend, iterations, 3, shots=shots, seed=seed + ) + + # get the circuit with the final parameters of the optimisation: + my_qaoa_circuit = qaoa_max_cut_circuit( + max_cut_graph_edges, n_nodes, best_mixer, best_cost + ) + + my_qaoa_circuit.measure_all() + + compiler_pass(my_qaoa_circuit) + handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed) + + result = backend.get_result(handle) + + return result + + +from pytket.extensions.qiskit import AerBackend + +backend = AerBackend() +comp = backend.get_compiled_circuit + +res = qaoa_calculate( + backend, + backend.default_compilation_pass(2).apply, + shots=5000, + iterations=1000, + seed=89023, +) + +from maxcut_plotting import plot_maxcut_results +print("hello") +plot_maxcut_results(res, 6) diff --git a/team_solutions/pineapple/maxcut_interpolate.py b/team_solutions/pineapple/maxcut_interpolate.py new file mode 100644 index 0000000..93707a6 --- /dev/null +++ b/team_solutions/pineapple/maxcut_interpolate.py @@ -0,0 +1,424 @@ +from matplotlib import pyplot as plt +from maxcut_plotting import plot_maxcut_results +from pytket.extensions.qiskit import AerBackend +from pytket.backends.backend import Backend +from pytket.backends.backendresult import BackendResult +from pytket.passes import DecomposeBoxes +from pytket.utils import gen_term_sequence_circuit +import numpy as np +from pytket import Qubit, Circuit +from pytket.pauli import QubitPauliString, Pauli +from pytket.utils import QubitPauliOperator +from typing import List, Tuple, Callable +import networkx as nx +import networkx.algorithms.isomorphism.vf2userfunc as vf2 +import networkx as nx +import numpy as np +import matplotlib.pyplot as plt +import networkx.algorithms.isomorphism.vf2userfunc as vf2 + + +def subgraphInduce(G, edge, depth, rename=True): + # return: subgraph induced by edge + current = set(edge) + edges = set([edge]) + for i in range(depth): + next = set() + for node in current: + next.update(G.neighbors(node)) + edges.update(G.edges(node)) + current.update(next) + if rename: + # Rename nodes to 0, 1, 2, ... such that 0 and 1 are the central edge. + current.remove(edge[0]) + current.remove(edge[1]) + current = [edge[0], edge[1]] + list(current) + + edges = [(current.index(e[0]), current.index(e[1])) + for e in edges] + return nx.Graph(list(edges)) + + +def gimme_subgraphs(): + res = [] + + for t in range(20, 100, 2): + G = nx.random_regular_graph(3, t) + for edge in G.edges(): + subgraph = subgraphInduce(G, edge, 2) + seen = False + for i in res: + if nx.is_isomorphic(subgraph, i): + seen = True + # res[i][1] += 1 + break + if not seen: + # print(len(res)) + # res.append([subgraph, 1]) + res.append(subgraph) + print("Generated Subgraphs len = ", len(res)) + return [list(i.edges) for i in res] + + +# res = sorted(res, key=lambda x: x[1], reverse=True) +# for i in range(100): +# print(res[i][1]) +# nx.draw(G) +# plt.show() + + +# Define graph. +# TOOD: Hardcoded for now, but should be able to take any p, k +# SUBGRAPHS = [ +# [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)], +# [(0, 1), (0, 2), (0, 3), (1, 2), (1, 4)], +# [(0, 1), (0, 2), (0, 3), (1, 4), (1, 5)], +# ] + + +SUBGRAPHS = gimme_subgraphs() +print(SUBGRAPHS[0]) +print(len(SUBGRAPHS)) + + +NX_SUBGRAPHS = [nx.Graph(e) for e in SUBGRAPHS] + +P_DISTANCE = 2 # TODO: Hardcoded for now, but should be able to take any p, k +K_BRANCHING = 3 # TODO: Hardcoded for now, but should be able to take any p, k + +DISCRETIZATION = (4, 4, 4, 4) + +n_nodes = 24 + + +max_cut_graph = nx.random_regular_graph(K_BRANCHING, n_nodes) + + +# nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()}) +# plt.show() + +max_cut_graph_edges = max_cut_graph.edges() + + +# max_cut_graph = nx.Graph() +# max_cut_graph.add_edges_from(max_cut_graph_edges) +# nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()}) + +# expected_results = [(0, 1, 0, 0, 1, 0, 0), (1, 0, 1, 1, 0, 1, 1)] + + +def get_subgraph_frequency(nx_graph): + """ + Find the frequency of each subgraph in the graph. + """ + subgraph_freq = np.zeros((len(NX_SUBGRAPHS),)) + for central_edge in nx_graph.edges: + neighborhood = subgraphInduce( + nx_graph, central_edge, P_DISTANCE) + for i, subgraph in enumerate(NX_SUBGRAPHS): + if nx.is_isomorphic(neighborhood, subgraph): + subgraph_freq[i] += 1 + print(subgraph_freq) + return subgraph_freq + + +def qaoa_graph_to_cost_hamiltonian( + edges: List[Tuple[int, int]], cost_angle: float +) -> QubitPauliOperator: + """ + This function takes a list of edges and a cost angle and returns a QubitPauliOperator + representing the cost Hamiltonian for the QAOA algorithm. + + """ + qpo_dict = {QubitPauliString(): len(edges) * 0.5 * cost_angle} + for e in edges: + term_string = QubitPauliString( + [Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z]) + qpo_dict[term_string] = -0.5 * cost_angle + return QubitPauliOperator(qpo_dict) + + +cost_angle = 1.0 +cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle) +print(cost_ham_qpo) + + +def qaoa_initial_circuit(n_qubits: int) -> Circuit: + c = Circuit(n_qubits) + for i in range(n_qubits): + c.H(i) + return c + + +def qaoa_max_cut_circuit( + edges: List[Tuple[int, int]], + mixer_angles: List[float], + cost_angles: List[float], +) -> Circuit: + """ + Create a QAOA circuit for the MaxCut problem. + """ + + n_nodes: int = len(set().union(*edges)) + + # print(len(mixer_angles), len(cost_angles)) + + assert len(mixer_angles) == len(cost_angles) + + # initial state + qaoa_circuit = qaoa_initial_circuit(n_nodes) + + # add cost and mixer terms to state + for cost, mixer in zip(cost_angles, mixer_angles): + cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost) + mixer_ham = QubitPauliOperator( + {QubitPauliString([Qubit(i)], [Pauli.X]) : mixer for i in range(n_nodes)} + ) + qaoa_circuit.append(gen_term_sequence_circuit( + cost_ham, Circuit(n_nodes))) + qaoa_circuit.append(gen_term_sequence_circuit( + mixer_ham, Circuit(n_nodes))) + + DecomposeBoxes().apply(qaoa_circuit) + return qaoa_circuit + + +def max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult, maximize=False) -> float: + """ + Given the results of all shots: + - if maximize is False, return the average energy of the distribution (used for training) + - if maximize is True, return the maximum energy of the distribution (used for testing) + """ + energy = 0.0 + dist = results.get_distribution() + if maximize: + for meas in dist.keys(): + energy = max(energy, sum((meas[i] ^ meas[j]) for i, j in edges)) + else: + for i, j in edges: + energy += sum((meas[i] ^ meas[j]) * + prob for meas, prob in dist.items()) + + return energy + + +def single_edge_energy(results: BackendResult) -> float: + """ + Return the expected enery of a edge 0-1. + """ + dist = results.get_distribution() + return sum((meas[0] ^ meas[1]) * prob for meas, prob in dist.items()) + + +def qaoa_instance_simple( + graph_edges: List[Tuple[int, int]], + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + guess_mixer_angles: np.array, + guess_cost_angles: np.array, + seed: int, + shots: int = 5000, +) -> float: + # step 1: get state guess + my_prep_circuit = qaoa_max_cut_circuit( + graph_edges, guess_mixer_angles, guess_cost_angles + ) + + measured_circ = my_prep_circuit.copy().measure_all() + compiler_pass(measured_circ) + res = backend.run_circuit(measured_circ, shots, seed=seed) + + return max_cut_graph_edges, res + + +def single_precompute(graph, backend, compiler_pass, shots, discretization, seed): + """ + This function precomputes the results for a small k-regular graphs for many angles. + For instance, if discretization = (10, 10, 10, 10) [param_num = 4] + Then we will return results for mixer_angles = (i/10, j/10) and + cost_angles = (k/10, l/10) for all i, j, k, l in {0, ..., 9} + These are formatted as [*mixer_angles, *cost_angles] + + """ + # print(discretization) + results = np.zeros(discretization) + for index in np.ndindex(*discretization): + true_index = np.array(index) / discretization # normalize to [0, 1] + mixer_angles = true_index[:len(index) // 2] + cost_angles = true_index[len(index) // 2:] + + # get the circuit with the final parameters of the optimisation: + circuit = qaoa_max_cut_circuit( + graph, mixer_angles, cost_angles + ) + + circuit.measure_all() + + compiler_pass(circuit) + handle = backend.process_circuit(circuit, shots, seed=seed) + res = single_edge_energy(backend.get_result(handle)) + if (sum(index[1:]) == 0): + print("Angles", mixer_angles, cost_angles) + # print("Angles", mixer_angles, cost_angles, "Energy:", res) + + results[index] = res + return results + + +def precompute(backend, compiler_pass, shots, discretization, seed): + # precompute the results for all graphs + """ + This function precomputes the results for a small k-regular graphs for many angles. + For now we only precompute for a hardcoded list of k = 3, p = 1. + Node 0 = j, Node 1 = k. + """ + # TODO: don't cheat. + x = get_subgraph_frequency(max_cut_graph) + + filter = [i != 0 for i in x] + print(sum(filter), " out of ", len(filter), " graphs are non-zero.") + + results = [] + + for graph, f in zip(SUBGRAPHS, filter): + if not f: + results.append(None) + continue + print("subgraph: ", graph) + x = single_precompute( + graph, backend, compiler_pass, shots, discretization, seed + ) + results.append(x) + print("Maximum Energy: ", np.max(x)) + + rft = np.fft.rfftn(x) + rft[4:, :, :, :] = 0 + rft[:, 4:, :, :] = 0 + rft[:, :, 4:, :] = 0 + rft[:, :, :, 4:] = 0 + + x_smooth = np.fft.irfftn(rft) + # Sum along last two axes ( we are left with mixer ) + visualize_x = np.sum(x, axis=(2, 3)) + visualize_x_smooth = np.sum(x_smooth, axis=(2, 3)) + + # Sum along the first two axes ( we are left with cost ) + visualize_y = np.sum(x, axis=(0, 1)) + visualize_y_smooth = np.sum(x_smooth, axis=(0, 1)) + + f, axarr = plt.subplots(2, 2) + axarr[0][0].imshow(visualize_x, cmap="hot", interpolation="nearest") + axarr[0][1].imshow(visualize_x_smooth, cmap="hot", + interpolation="nearest") + axarr[1][0].imshow(visualize_y, cmap="hot", interpolation="nearest") + axarr[1][1].imshow(visualize_y_smooth, cmap="hot", + interpolation="nearest") + + plt.show() + + return results + + +backend = AerBackend() +comp = backend.get_compiled_circuit + + +PRECOMPUTE = precompute(backend, + backend.default_compilation_pass(0).apply, + 5000, DISCRETIZATION, 12345) + + +input() + + +def optimize_params(nx_graph, discretization): + """ + This function optimizes the parameters for a given graph. + """ + best = -np.inf + best_mixer_angles = None + best_cost_angles = None + + freq = get_subgraph_frequency(nx_graph) + print(freq) + for index in np.ndindex(*discretization): + score = sum(PRECOMPUTE[sub][index] * freq[sub] + for sub in range(len(SUBGRAPHS)) if freq[sub] != 0) + # print(score) + # print(best) + if (score > best): + best = score + + # normalize to [0, 1] + true_index = np.array(index) / discretization + best_mixer_angles = true_index[:len(index) // 2] + best_cost_angles = true_index[len(index) // 2:] + return best_mixer_angles, best_cost_angles + + +def calculateEnergy( + graph, + backend: Backend, + compiler_pass: Callable[[Circuit], bool], + shots: int = 5000, + seed: int = 12345, +) -> float: + """ + Given a graph, first compute the best parameters for the graph. + Then, run the QAOA circuit with the best parameters. + """ + + # find the parameters for the highest energy + nx_graph = nx.Graph(graph) + best_mixer, best_cost = optimize_params(nx_graph, DISCRETIZATION) + + print("Best parameters: ", best_mixer, best_cost) + + # get the circuit with the final parameters of the optimisation: + circuit = qaoa_max_cut_circuit( + graph, best_mixer, best_cost + ) + + circuit.measure_all() + + compiler_pass(circuit) + handle = backend.process_circuit(circuit, shots, seed=seed) + + result = backend.get_result(handle) + + return result + + +if __name__ == "__main__": + res = calculateEnergy( + max_cut_graph_edges, + backend, + backend.default_compilation_pass(2).apply, + shots=100000, + seed=12345 + ) + + dist = res.get_distribution() + energy_distribution = {} + energy_colors = {} + for meas in dist.keys(): + energy = sum((meas[i] ^ meas[j]) for i, j in max_cut_graph_edges) + if energy not in energy_distribution: + energy_distribution[energy] = 0 + energy_colors[energy] = [] + energy_distribution[energy] += dist[meas] + energy_colors[energy].append(meas) + + for i in sorted(energy_distribution.keys()): + print(i, energy_distribution[i]) + + best = max(energy_distribution.keys()) + print("Best energy: ", best) + coloring = energy_colors[best][0] + nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes( + )}, node_color=["red" if coloring[node] else "blue" for node in max_cut_graph.nodes()]) + plt.show() + + # print(res) + + # plot_maxcut_results(res, 10) diff --git a/team_solutions/pineapple/maxcut_notebook-optimizecompilation-binarylifting.ipynb b/team_solutions/pineapple/maxcut_notebook-optimizecompilation-binarylifting.ipynb new file mode 100644 index 0000000..06edbf2 --- /dev/null +++ b/team_solutions/pineapple/maxcut_notebook-optimizecompilation-binarylifting.ipynb @@ -0,0 +1,1648 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3ba3449", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The Quantum Approximate Optimisation Algorithm (QAOA) using TKET, optimizing Compilation Time" + ] + }, + { + "cell_type": "markdown", + "id": "45668632", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## The Max-Cut problem" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9456fbeb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "G = nx.Graph()\n", + "G.add_edges_from([(0,1), (1,2), (2,0)])\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, node_color=['red', 'blue', 'red'])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cad41481", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "There are \\\\(2^3\\\\) possible assignments of colour to nodes. In general there are \\\\( 2^n \\\\). The Max-cut problem can then be stated as that of finding the colour assignment which maximises the number of edges between vertices of a different colour." + ] + }, + { + "cell_type": "markdown", + "id": "7389bbe5", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Quantum Approximate Optimization Algorithm (QAOA)\n", + "\n", + "Introduced in 'A Quantum Approximate Optimization Algorithm' (found at https://arxiv.org/abs/1411.4028). The idea is to prepare a quantum state which encodes a solution to the Max-cut problem.\n", + "\n", + "\n", + "This is a variational algorithm, which is to say that a paramaterised state is prepared, with the parameters varied to improve the solution. We will have $2p$ parameters where p is our number of layers. In particular, the state prepared has the form \n", + "\n", + "\n", + "\n", + "\\\\[ \\left| \\psi \\left( \\beta, \\gamma \\right) \\right\\rangle = U \\left( \\beta_m \\right) U \\left( \\gamma_m \\right) ... U \\left( \\beta_0 \\right) U \\left( \\gamma_0 \\right) \\left| \\psi_0 \\right\\rangle \\\\]\n", + "where\n", + "\\\\[ U \\left( \\beta_i \\right) = e^{i \\beta H_B} \\quad \\& \\quad U \\left( \\gamma_i \\right) = e^{i \\gamma H_P} \\\\]\n", + "with \\\\( H_B \\\\) and \\\\( H_P \\\\) depending on the problem instance. " + ] + }, + { + "cell_type": "markdown", + "id": "3596e66d", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "d720b387", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "For the previous 3 vertex graph the *problem Hamiltonian* is\n", + "\\\\[ H_P = \\frac{1}{2} \\big[ \\left( Z \\otimes Z \\otimes I \\right) + \\left( Z \\otimes I \\otimes Z \\right) + \\left( I \\otimes Z \\otimes Z \\right) \\big] \\\\]\n", + "\n", + "\n", + "where you will notice that there is a \\\\( Z \\otimes Z \\\\) acting between each vertex which is connected by an edge." + ] + }, + { + "cell_type": "markdown", + "id": "8ca2e1b9", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "The *mixer Hamiltonian* has the form \n", + "\\\\[ H_B = \\left( X \\otimes I \\otimes I \\right) + \\left( I \\otimes X \\otimes I \\right) + \\left( I \\otimes I \\otimes X \\right) \\\\]\n", + "\n", + "\n", + "where you will notice that there is an \\\\( X \\\\) acting on each vertex." + ] + }, + { + "cell_type": "markdown", + "id": "de6b9e03", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "\n", + "A solution to maxcut can be found by maximising the following cost function $C$ .\n", + "\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here $x_i$ and $x_j$ are the the \"colours\" of each vertex. \n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i,x_j \\in \\{0,1\\}\n", + "\\end{equation}\n", + "$$\n", + "\n", + "$x_i(1-x_j)=0$ if $x_i=x_j$ and $ x_i(1-x_j)=1$ if the terms are not equal." + ] + }, + { + "cell_type": "markdown", + "id": "61d4e798", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We want to encode our Maxcut cost function as a Hamiltonain. To do this we can perform the following translation.\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i \\mapsto \\frac{1}{2}(1-Z_i)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "The Pauli Z operator can be used to distinguish between the $|0\\rangle$ and $|1\\rangle$ basis states as these are eigenstates with eigenvalues $\\pm 1$ .\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_B = \\sum_i X_i\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here we use the the convention that $X_i$ means a Pauli X operator will be applied to the \"ith\" qubit and the identity operator will be applied to all other qubits in the circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "aeb6abd9", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "id": "3cbb783a", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Circuit Construction for QAOA" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "688f1332", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAb4AAAEuCAYAAADx63eqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAAsTAAALEwEAmpwYAAAoF0lEQVR4nO3de1RUZ7om8KduUiAgiqhc1QQF71Ekoka5eEtMTGI0ikqiVFUnvUz3menpmdUX50yfWXPs1T3pWb265yST06kqNRHQiCZRY2I0XCIxeE2wc6QwxEZBAQUtkUtBXfb8Yaoi4SJgVe1dVc9vLf+watf2ZSk+fO/e77dlgiAIICIiChBysQsgIiLyJgYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFAYfEREFFKXYBZD3NLV2ovBcHUwNLWix2BCuViJ5XDheTIlDZGiQ2OUREXmFTBAEQewiyLMqas14o6QapZduAgA6bQ7Xe2qlHAKAjKQobE1PxKz4CHGKJCLyEgafn9tdXoPtR0yw2Ozo729aJgPUSgW2rUxGTtoEr9VHRORtbHX6sXuhV4kOq+OBxwoC0GG1Y/uRSgBg+BGR3+KKz09V1JqR/XY5Oqz2Hu+1XSyF+YsC2FtuQjF8JCKf/s9Qx093vR+sUmDvK2mYGRfhxYqJiLyDKz4/9UZJNSy2nqHX8Y+vcLtkJ6Ke+xWGxUyGvfVWj2MsNjveLKnGWzlzvVEqEZFXMfj8UFNrJ0ov3ez1mt6dsjyMWLgBQbHJAABl2OgexwgCUFx1E82tnbzbk4j8DoPPDxWeq+v1dcFhR2d9NYIT5+HaWz+BYO9CyKQ0RGRqIFd1DzgZgMLzdXh18aNeqJiIfI0vj0cx+PyQqaGl28iCk73NDDhsaK/6AmNz/giZXIGb+/8Vd07uxcj0l7sda7E5YKq/66WKichX9D8e1YA/H78k+fEo7tzih1ostl5fl32/qgtLWQVl6CgoQkYgLPV5dHx3to/zWD1WIxH5nt3lNch+uxzHKhvRaXP0+AHb8v1rn15sRPbb5dhdXiNOoQ/AFZ8fClf3/teqUIdC8aNrejKZrJ/zqNxaFxH5Ln8aj2Lw+aHkceEIUjb02u4MnbEUd88dRvAjKYBCiZYzHyAkMbXHcQo4EBfWdygSUeCoqDVj+xFTj9BryPs1Oq9XQSZXAAAUYZGIfeXfXe93WB3YfsSEmXERkhqPYvD5obUpcfjz8Uu9vjdiYTbsHS249rdXIVOqMDx5EUYsWN/jOEEQ8K+aZ/DFglRoNBo8+eSTUCr5z4UoEPU1HgUAo5b/FGGzVvT5WSmOR/Eanx8aHRqE9MlR6K2LKVMoEbliKxJ+sRfxP9+NUctehUw5rPsxMmD59BhcufQfWLlyJX7/+98jISEBv/nNb3DpUu+BSkT+qb/xqIG4fzxKKhh8fuq1jESolYohfVatVGBrRiLCw8Oh0+lw8uRJHD9+HDabDYsXL8aiRYuwY8cOtLa2urlqIpKavsajnMwlu1D7l41oePe/wXLlQq/HOMejpILB56dmxUdg28pkBKsG91ccrJJj28rkHv34qVOn4vXXX0dtbS1++ctf4v3330d8fLwrGLnzHZF/6ms8CgBGZuYi9qd6xL22C6GPPYkb+/8XrLfrexwntfEoBp8fy0mbgG0rpyBYpei17Xk/mezeHp3bVk7p9w4slUqF559/HgcPHsTFixcxadIk5ObmuoKxoaHBvV8EEYmqr/EoAAiKSYI8KAQypQqhM5YgKHaKT4xHMfj8XE7aBOx9JQ0rpo5FkFIOtbL7X7laKUeQUo4VU8di7ytpg7rtODo6Gr/61a9gMpmg1+thMpkwZcoUPPfcczh48CCsVun8QyeiwbNYLOi40zzwD8hkAHrv/khpPIpPZwggza2dKDxfB1P9XbRYrAhXq5AcHYa1c9y3xdDdu3exb98+GI1GfPfdd3jppZeg0WiQnJzslvMTkedYrVacPXsWRUVFKCoqwqlTpzDhqZ+gIzETdnS/Z8BhaUXn9SqoE2YAcgXaKj/HrU/+DdG5f4VqVGy3Y9VKOX6xbLJktkBk8JHHVFVVwWg04p133sEjjzwCjUaDdevWISwsTOzSiAiA3W5HRUUFioqKUFxcjLKyMkycOBFZWVnIysrCokWLYFWosfCPRT2u89nb7+DGe/8C6606QCaHKjIOEYtyEDxxdo8/J0gpx8lfZUlmD08GH3mc1WrFJ598AqPRiJKSEqxevRoajQYLFy7sd+cYInIvQRBQWVnpWtGVlJRg7NixrqBLT0/H6NE9n9jyyrtncayycUgjDTIZsGLqWEnN8TH4yKsaGxvx7rvvwmAwwG63Q6PRYPPmzYiOjha7NCK/IwgCLl++7Aq64uJihISEICsrC5mZmcjMzERMTMwDz9Pfg60fRIoPtmbwkSgEQUB5eTmMRiMKCwvxxBNPQKPR4JlnnoFKJZ2L4ES+pq6uzhVyRUVFsFqtrhVdZmYmJk6cOKTzDmavTqd741H93ykuBgYfia6trc11Q0xVVZXrhpipU6eKXRqR5N24cQMlJSWuVd2tW7eQmZnpCrvJkye77ZLCvfAzwWKz99v2lMnubYSxbWWy5EIPYPCRxFy6dAk7d+7Erl27kJCQAI1Gg/Xr1yM8PFzs0ogkwWw2o7S01BV0tbW1WLx4sWtFN2PGDMjlnptUu1Bnxpsl1SiuugkZ7g2nO6mVcggAMpOisDUjUVLtzfsx+EiSbDYbjh49CqPRiM8++wzPPfcctFotFi1axBtiKKC0trairKzM1bo0mUyYP3++a0U3Z84cUTaQ98Z4lKcw+Ejybty4gd27d8NgMKCrqwu5ubnYvHkzYmNjH/xhIh9jsVhQXl7uWtF9/fXXSElJcQXd448/jqAgaQeL1DH4yGcIgoAzZ87AYDBg3759mD9/PjQaDVatWoVhw4Y9+AREEtTb0Pj06dNdrcuFCxciJCRE7DL9CoOPfFJ7ezv2798Pg8GAixcvYtOmTdBqtZg+fbrYpRH1yzk07mxd9jY0PmLECLHL9GsMPvJ51dXV2LlzJ3bu3ImYmBhotVpkZ2fzPw+ShKEOjZPnMPjIb9jtdnz66acwGo04duwYVq1aBY1Gg/T0dI/e5UZ0P3cNjZPnMPjILzU1NSEvLw8GgwFtbW2uG2Li4+PFLo38UF1dnat16c6hcfIMBh/5NUEQcO7cORiNRuzduxepqanQarV49tlneWccDZk3h8bJ/Rh8FDA6Ojpw4MABGI1GXLhwARs3boRWq8XMmTPFLo0krq+hcWfYeXponNyLwUcB6R//+Ad27NiBnTt3YsyYMdBoNNiwYQNGjhwpdmkkAVIdGif3YPBRQLPb7fjss89gMBhw9OhRPP3009BoNMjMzORP8AGEQ+OBhcFH9L3m5mbk5+fDYDDAbDYjNzcXW7Zswfjx48Uujdzs/qHx4uJinDp1CtOmTXO1Ljk07t8YfES9OH/+PIxGIwoKCpCSkgKNRoPnn38earVa7NJoCBwOh+tJ4xwaJwYfUT8sFgs++OADGAwGfPXVV9iwYQM0Gg1mz54tdmnUjx8PjZeWlmLMmDEcGicADD6iAaupqcGuXbuwY8cOjBo1ChqNBhs3bsSoUaPELi3gcWicBoPBRzRIDocDRUVFMBqNOHLkCJ588klotVosWbKEN8R4EYfGaagYfEQP4fbt28jPz4fRaERTUxO2bNmCLVu28D9dD+DQOLkLg4/ITb7++mvs2LED+fn5mDVrFjQaDVavXo3g4GCxS/NJ9w+NFxcX4+rVqxwaJ7dg8BG5mcViwcGDB2E0GnHmzBmsX78eWq0Wc+bM4YqkH21tbSgrK3Ot6Dg0Tp7C4CPyoKtXr7puiAkLC4NGo0FOTg4iIyPFLk10HBonsTD4iLzA4XCgtLQUBoMBhw8fxvLly6HRaLBs2TIoFAqxy/MKDo2TVDD4iLzMbDZjz549MBgMaGhowJYtW5Cbm4tHHnlE7NLcikPjJFUMPiIRXbhwATt27EBeXh6mTZsGrVaLF1544aFWPk2tnSg8VwdTQwtaLDaEq5VIHheOF1PiEBnqudYhh8bJVzD4iCSgs7MThw4dgtFoRHl5OdatWweNRoPU1NQB3xBTUWvGGyXVKL108945bQ7Xe2qlHAKAjKQobE1PxKz4iIeu+f6hcec8XUhIiKt1yaFxkioGH5HE1NXV4Z133oHRaERwcLDrhpioqKg+P7O7vAbbj5hgsdnR33e0TAaolQpsW5mMnLQJQ6qNQ+Pk6xh8RBLlcDhw4sQJGI1GfPjhh1i6dCk0Gg2WL1/e7bb+e6FXiQ6ro5+zdReskmPbyikPDD8OjZM/YvAR+YA7d+5g7969MBqNqKurw+bNm5Gbm4u2oNHIfrscHVZ7r5+z3rqG64afYXjyQoxe9V+7vResUmDvK2mYGRfhes05NO5c1XFonPwRg4/Ix3zzzTfYsWMHdu/ejVHP/RqWyEkQ0Puqq3HPP0OwdUI5YkyP4JPJgCWTR+PFaDOHximgMPiIfFT9rVYs+lMJbELvodd2sRTtl76EKjIeNnN9j+ADAMHWhfjz/45li+cjMzMT8+bN49A4+T3+KEfkoz78eyMUCgVstp7X9hyd7TCfyMPYDb9Ha8XRPs+hVqvx0u/ewKuLH/VkqUSSwmY9kY8yNbR0G1m4n/nzdxE6azmU4f3PzXXaHDDV3/VEeUSSxeAj8lEtFluvr3c1XoblSgXCU58b4Hms7iyLSPLY6iTyUeHq3r99LVf/DtudRtS9mQsAELosgOBAfdN/QnTuX3o5j8qjdRJJDYOPyEcljwtHkLKhR7sz9LEVGD5lsev3LacPwHanEaNWvNbjHGqlHMnRYR6vlUhK2Ook8lFrU+J6fV2uUkMROtL1S6ZSQ6YcBkVIzw2hBQBr5/R+HiJ/xRUfkY8aHRqE9MlROFbZ2O82ZRGLNvX6ukwGZCZFeXTjaiIp4oqPyIe9lpEItXJoz/NTKxXYmpHo5oqIpI/BR+TDZsVHYNvKZASrBvetfG+vzuRu25URBQq2Ool8nHOj6e1HTLBY7ehvK6aHfToDkT/glmVEfuJCnRmv/mU/bijHQKVUwtLL8/gyk6KwNSORKz0KaAw+Ij9hs9kQFxeHQ58W47x5GEz1d9FisSJcrUJydBjWzvHsE9iJfAVbnUR+orS0FHFxcUidOQWpYhdDJGG8uYXITxQUFGDDhg1il0EkeWx1EvmBzs5OxMTEoKKiAnFxHEgn6g9XfER+4OjRo5g+fTpDj2gAGHxEfoBtTqKBY6uTyMe1tbUhNjYW1dXVGD26/+fvERFXfEQ+7+DBg1iwYAFDj2iAGHxEPo5tTqLBYauTyIfdunULEydORF1dHcLC+Fw9ooHgio/Ihx04cADLly9n6BENAoOPyIexzUk0eGx1Evmo+vp6TJs2DdevX4darRa7HCKfwRUfkY9677338OyzzzL0iAaJwUfko9jmJBoatjqJfNDly5eRlpaGa9euQaVSiV0OkU/hio/IB+3Zswdr165l6BENAYOPyAexzUk0dAw+Ih/zzTffwGw2Y+HChWKXQuSTGHxEPqagoADZ2dmQy/ntSzQUvLmFyIcIgoDExETs27cPc+bMEbscIp/EHxmJfMjp06ehVCoxe/ZssUsh8lkMPiIf4rypRSaTiV0Kkc9iq5PIR9jtdsTHx6O4uBhJSUlil0Pks7jiI/IRpaWlGDduHEOP6CEx+Ih8BGf3iNyDrU4iH9DV1YXo6Gh89dVXSEhIELscIp/GFR+RD/j0008xdepUhh6RGzD4iHwA25xE7sNWJ5HEtbe3IyYmBt9++y2ioqLELofI53HFRyRxhw4dQlpaGkOPyE0YfEQSxzYnkXux1UkkYWazGePHj8fVq1cxYsQIscsh8gtc8RFJ2IEDB7BkyRKGHpEbMfiIJIxtTiL3Y6uTSKIaGhqQnJyM+vp6BAcHi10Okd/gio9Iovbt24dVq1Yx9IjcjMFHJFFscxJ5BludRBJUU1OD1NRUXL9+HSqVSuxyiPwKV3xEErRnzx6sWbOGoUfkAQw+Iglim5PIcxh8RBJz8eJFNDc3Y9GiRWKXQuSXGHxEElNQUID169dDLue3J5EnKMUugIh+IAgCCgoKsGfPHrFLIfJb/JGSSELOnj0LmUyGlJQUsUsh8lsMPiIJcd7UIpPJxC6FyG9xjo9IIux2OxISEnD8+HFMmTJF7HKI/BZXfEQSceLECURFRTH0iDyMwUckEZzdI/IOtjqJJKCrqwsxMTE4d+4cxo8fL3Y5RH6NKz4iCTh27BiSkpIYekRewOAjkgC2OYm8h61OIpG1t7cjJiYGVVVVGDt2rNjlEPk9rviIRPbRRx8hNTWVoUfkJdyyjMiLmlo7UXiuDqaGFrRYbAhXK3H602+w4cWNYpdGFDDY6iTygopaM94oqUbppZsAgE6bw/WeYO1EkFqNzOQx2JqeiFnxESJVSRQYGHxEHra7vAbbj5hgsdnR33ebTAaolQpsW5mMnLQJXquPKNCw1UnkQfdCrxIdVscDjxUEoMNqx/YjlQDA8CPyEK74iDykotaM7LfL0WG1d3u96dCfYKmpgMNqgWL4SISnrUHYrBXdjglWKbD3lTTMjIvwYsVEgYHBR+Qhr7x7FscqG3u0N7tuXoFqZAxkShWszbVoyP8Nxrz4Lwgal+g6RiYDVkwdi7dy5nq5aiL/x3EGIg9oau1E6aWbvV7TGxY1HjKl6vvfySCDDLbb9d2OEQSguOommls7PV8sUYDhNT4iDyg8V9fv+81H30Tb3z+DYOvEsLGPIvjRnis7GYDC83V4dfGjHqqSKDAx+Ig8wNTQ0m1k4cciV2zFqGWvovOaCZarf4dMoepxjMXmgKn+rifLJApIbHUSeUCLxfbAY2RyBdTx02C/24S7Xx3p4zxWd5dGFPAYfEQeEK4eRDPF4ehxje+H8/RcCRLRw2HwEXlA8rhwBCl7fnvZ28xou1gKR1cHBIcdHZfPoa2yFOoJj/U4Vq2UIzk6zAvVEgUWXuMj8oC1KXH48/FLPd+QyXD3q4/RfPRNQHBAOWIMRi75CUImzetxqABg7Zw4zxdLFGAYfEQeMDo0COmTo3rM8SlCRmDcpj888PMyGZCZFIXI0CAPVkkUmNjqJPKQ1zISoVYqhvRZtVKBrRmJDz6QiAaNwUfkIbPiI7BtZTKCVYP7NgtWybFtZTK3KyPyELY6iTzIudH09iMmWKx29Lc/IJ/OQOQd3KuTyAsu1JmR+/oemENioVIqYblvuF2tlEPAvWt6WzMSudIj8jAGH5EXtLe3IyYmBuVffYPS2k6Y6u+ixWJFuFqF5OgwrJ0TxxtZiLyErU4iLzh06BDmzZuH5IlxSJ4odjVEgY03txB5QX5+PjZu3Ch2GUQEtjqJPO7WrVuYOHEiamtrER4eLnY5RAGPKz4iDyssLMSKFSsYekQSweAj8jC2OYmkha1OIg+qra3FY489huvXryMoiHdtEkkBV3xEHrRnzx688MILDD0iCWHwEXkQ25xE0sPgI/KQixcv4saNG1i8eLHYpRDRfRh8RB6Sn5+PDRs2QKEY2hMaiMgzeHMLkQcIgoBHH30UhYWFmDNnjtjlENF9uOIj8oBTp05h2LBhmD17ttilENGPMPiIPCAvLw+bNm2CTCYTuxQi+hG2OonczGazITY2Fl988QUSE/kUdSKp4YqPyM0+++wzTJgwgaFHJFEMPiI34+wekbSx1UnkRu3t7YiNjUVlZSXGjRsndjlE1Auu+Ijc6PDhw0hNTWXoEUkYg4/IjdjmJJI+tjqJ3IQPnCXyDVzxEbnJ/v37sXz5coYekcQx+IjchG1OIt/AVieRG9TV1WHWrFl84CyRD+CKj8gN9uzZg9WrVzP0iHwAg4/IDdjmJPIdDD6ih1RZWYnGxkakp6eLXQoRDQCDj+ghFRQUIDs7mw+cJfIRvLmF6CEIgoDExES89957SElJEbscIhoArviIHsLp06ehVCr5lHUiH8LgI3oI+fn5fOAskY9hq5NoiGw2G+Li4nDixAlMmjRJ7HKIaIC44iMaoqKiIiQkJDD0iHwMg49oiJxtTiLyLWx1Eg1BR0cHYmJicPHiRURHR4tdDhENAld8RENw+PBhzJ07l6FH5IMYfERDwC3KiHwXW51Eg3T79m1MmDABV69exYgRI8Quh4gGiSs+okHav38/li1bxtAj8lEMPqJBYpuTyLex1Uk0CNeuXcOMGTNw/fp1qNVqscshoiHgio9oEPbu3YvVq1cz9Ih8GIOPaBDy8vLY5iTycQw+ogEymUyor69HRkaG2KUQ0UNg8BENEB84S+QfeHML0QAIgoBJkyZhz549mDt3rtjlENFD4IqPaADOnDkDuVzOp6wT+QEGH9EA8IGzRP6DrU6iB7Db7YiLi0NpaSkmT54sdjlE9JC44iN6gKKiIsTFxTH0iPwEg4/oAfjAWSL/wlYnUT/4wFki/8MVH1E/PvroI6SkpDD0iPwIg4+oH3wSA5H/UYpdAJFUNLV2ovBcHUwNLWix2KCWCyhrVuP/PPWs2KURkRvxGh8FvIpaM94oqUbppZsAgE6bw/WezGHDsGHDkJEUha3piZgVHyFSlUTkLgw+Cmi7y2uw/YgJFpsd/X0nyGSAWqnAtpXJyEmb4LX6iMj92OqkgHUv9CrRYXU88FhBADqsdmw/UgkADD8iH8YVHwWkilozst8uR4fV7nqt5dwhtP39M3TdrMHwKekY/cwvev1ssEqBva+kYWZchJeqJSJ34l2dFJDeKKmGxWbv9poyNBIjFqxH6Mxl/X7WYrPjzZJqT5ZHRB7E4KOA09TaidJLN3tc0wtJWoCQyfMhDw7v9/OCABRX3URza6cHqyQiT2HwUcApPFf30OeQASg8//DnISLvY/BRwDE1tHQbWRgKi80BU/1dN1VERN7E4KOA02Kxuek8Vrech4i8i+MMFBAEQUBlZSWKi4vx9UUbEJ740OcMV6vcUBkReRuDj/ySIAj47rvvUFRUhOLiYhQXFyM4OBhZWVmYO/1JlDTL0GXvfneL4LADzl+CA4KtC5ArIJMrepxfJQcmjw311pdDRG7EOT7yG1evXnUFXVFRERwOB7KyspCZmYnMzExMnDgRwL27Ohf+sajHdT7ziTzc+aKg22sjFm5AxKJensVnt8J+4DfI3fgicnNzXecmIulj8JHPqq+vd63mioqKcPfuXVfIZWVlYdKkSZDJZL1+9pV3z+JYZWO/25T1RSYDVkwdi60zh8FgMCAvLw+zZ8+GTqfD888/j6CgoIf8yojIkxh85DOamppQUlLiCrrGxkakp6e7VnXTpk3rM+h+rLedWwbqxzu3WCwWfPDBB9Dr9aioqMCmTZug1WoxY8aMQZ+biDyPwUeSZTab8fnnn7uCrqamBk888YQr6GbNmgWFouf1t4EazF6dTsEqObatnNLnXp2XL1+G0WjEzp07ERsbC51Oh+zsbISFhQ25TiJyLwYfSUZrayvKyspcQWcymZCWluYKupSUFKhU7r2T0lNPZ7DZbDh69CgMBgOKi4uxevVq6HQ6zJ8/f8CrUiLyDAYfiaajowNffvmlK+gqKiqQkpLiCrp58+Z55XrZhToz3iypRnHVTchwbzjdSa2UQwCQmRSFrRmJQ9qYuqGhAe+88w4MBgMUCgW0Wi1efvllREVFue1rIKKBY/CR13R1deH06dOuOy/PnDmDGTNmuIJuwYIFCAkJEa2+5tZOFJ6vg6n+LlosVoSrVUiODsPaOXGIDH34ABYEAWVlZdDr9fjwww+xbNkyaLVaLFu27KFatkQ0OAw+8hibzYbz58+7gu7kyZOYPHmyK+gWLVoUsNe+7ty5g4KCAuj1ety4cQMajQa5ubkYP3682KUR+T0GH7mNw+HAhQsXXEF34sQJJCQkuEYM0tPTMXLkSLHLlJyvv/4aBoMB+fn5mDt3LnQ6HZ599lmORRB5CIOPhsy5DZgz6EpKShAVFeWao0tPT8eYMWPELtNndHR04P3334der8c333yDnJwcaLVaTJs2TezSiPwKg48GTBAEVFdXu25GKSkpQUhIiCvoMjIyEBsbK3aZfuG7776D0WjEjh07MH78eOh0Oqxfvx6hodwmjehhMfioX1euXHEFXXFxMQRBcAVdZmYmJkyYIHaJfs1ms+Hjjz+GwWBAaWkp1qxZA51Oh3nz5nEsgmiIGHzUjXMbMGfQObcBcwZdf9uAkWfV19dj165dMBgMCAoKglarxUsvvYTRo0eLXRqRT2HwBTjnNmDOoGtsbERGRoYr7KZOncqgkxhBEPD555/DYDDg4MGDWLFiBbRaLZYuXQq5nI/YJHoQBl+AMZvNKC0tdW3uXFNTg0WLFrmCbubMmZwp8yFmsxn5+fnQ6/W4deuWaywiPj5e7NKIJIvB5+ec24A5V3Qmkwnz5893jRh4YhswEsf58+dhMBhQUFCAefPmQafTYdWqVRg2bJjYpRFJCoPPzzi3AXMGXUVFBebOneta0T3++OOcD/Nz7e3tOHDgAPR6PSorK/HSSy9Bq9ViypQpYpdGJAkMPh/X1dWFU6dOuW5IOXv2LGbOnOkKuvnz54u6DRiJ69tvv3U9LeKRRx6BTqfDunXrMHz4cLFLIxINg8/H2Gw2nDt3zhV0X375JZKSklxB98QTTwTsNmDUN6vVio8//hh6vR5lZWVYu3YtdDodUlNTefMSBRwGn8Q5HA5UVFS4gq6srMy1DVhWVhYWL17MbcBoUK5fv+4aiwgJCYFWq0VOTg4iIyPFLo3IKxh8EiMIAi5evOgKutLSUkRFRbnm6DIyMvg4G3ILh8OB0tJSGAwGHD58GE899RS0Wi2ysrI4FkF+jcEnMuc2YM6bUYqLizF8+HBX0GVmZiImJkbsMsnP3b59G3l5edDr9bhz5w60Wi22bNmCuLg4sUsjcjsGnwiuXLniCrqioiIA6BZ03AaMxCIIAs6fPw+9Xo+9e/di/vz50Ol0eOaZZzj2Qn6DwecF169fd63mioqK0NbW5gq5rKwsJCYm8gYDkpz29nYUFhZCr9fj0qVLePnll6HVapGUlCR2aUQPhcHnATdv3kRJSYkr6G7cuMFtwMinVVVVwWg0YteuXZg0aRJ0Oh3Wrl3LsQjySQw+N7h/G7CioiJcuXKF24CRX7Jarfjoo4+g1+tx8uRJrFu3DjqdDikpKfxhjnwGg28I7t69i7KyMlfQVVVVYf78+a7rdCkpKVAqlWKXSeRR165dw86dO2EwGBAeHg6tVotNmzZh1KhRYpdG1C+fCb6m1k4UnquDqaEFLRYbwtVKJI8Lx4spcYgM9ewWXB0dHTh58qQr6C5cuIC5c+e6go7bgFEgczgcKC4uhsFgwJEjR/D0009Dq9UiIyODYxEkSZIPvopaM94oqUbppZsAgE6bw/WeWimHACAjKQpb0xMxKz7CLX+mcxsw552Xzm3AnEG3YMECBAcHu+XPIvInzc3NrrGItrY2aLVabN68GbGxsWKXRuQi6eDbXV6D7UdMsNjs6K9KmQxQKxXYtjIZOWkTBv3nOLcBcwadcxswZ9BxGzCiwREEAWfPnoVer8e+ffuwcOFC6HQ6rFy5kmMRJDrJBt+90KtEh9Xx4IO/F6ySY9vKKQ8MP+c2YM6gO3HiBCZMmOAaMeA2YETu09bWhn379sFgMKC6uhqbN2+GVqvFpEmTxC6NApQkg6+i1ozst8vRYbW7XhNsVjR/+iYsNV/DYWmFMmIcRqZvRvCjc7t9NlilwN5X0jAzLuKHz36/DZgz6O7fBiwrKwvp6encBozIC0wmEwwGA9555x0kJydDp9NhzZo1fIIIeZUkg++Vd8/iWGVjt/amo8uCllP7ETpjKRQjotDx3Vk0HXwdMZp/gzJirOs4mQxYPnUsfvl4uOtmlJKSEoSGhrpWdNwGjEhcXV1dOHz4MPR6PU6dOoX169dDp9Nhzpw5YpdGAUBywdfU2omFfyzqdhNLX64bfoYRCzdgePLCbq8LNivsB36NrIWPu67TjR8/3lMlE9FDqK2tdY1FjBo1ClqtFhs3buTlBvIYyQXfW6Xf4c/HLz0w+Oxtt1H3pgYxmr9CFRnf7b1hChn+y7Ik/DT9UU+WSkRu5HA4UFRUBL1ej08++QSrVq2CVqtFeno6h+PJrSQ3ZW1qaHlg6Al2G5oO/gmhM5b0CD0A6LILqGq466kSicgD5HI5li5diqVLl6K5uRm7d+/Gz3/+c1gsFmg0GmzZsgXR0dFDPr+Ys8AkLZJb8Wl2nUGR6Uaf7wuCA00HX4ejsx1j1vwzZIres3tJ8hgYNqd6qkwi8gJBEHD69GkYDAbs27cPixcvhk6nw1NPPTXg3ZHEmAUmaZPctgrh6r7/MQuCgOYjf4W9zYyo1b/tM/TunYezQkS+TiaTYd68efjb3/6G2tpaPPfcc/jDH/6AhIQE/Pa3v0V1dXW/n99dXoPst8txrLIRnTZHj26S5fvXPr3YiOy3y7G7vMaDXw1JheSCL3lcOIKUvZd16+gbsDbXYsza/wG5qu/WhFopR3I0B86J/EloaCg0Gg2++OILHD9+HJ2dnViwYAEyMzORl5eHjo6Obsf/MAvc/wYYACAIQIfVju1HKhl+AUByrc6+7uq03bmBa/9PAyhUkMl/eNLBqCdfQ+i0zG7HBinlOPmrLPbtifxcV1cXDh48CL1ej7NnzyI7Oxs6nQ6yyAk9ZoEBwN5xF81H/gJLzVeQB4djZPpmDJ+W0e2Y3maByb9ILviA3uf4BkomA1ZMHYu3cuY++GAi8htXr17Fjh07YDQaEbTkZ+gaOwVA97tBb374vwFBQOTKf0JX42XcKPyfGJfzOoZF/TDuxP9D/J/kWp0A8FpGItTKoT2/Tq1UYGtGopsrIiKpS0hIwO9+9zucvlAJR/RU/Dj0HF0WtFedRMTiHMiHBUMdPw0hifPQ9h/F3Y4TBKC46iaaWzu9WD15kySDb1Z8BLatTEawanDl3durM5ktCqIA9v7X9VD08jgk261rkMkVUI364UkRqjETYb15pcexMgCF5+s8WSaJSHJzfE7Ojaa98XQGIvIffc0CO6wdkAV1f5yYPCgEjq6OHsdabA6Y6jkL7K8kG3zAvfCbGReBN0uqUVx1EzLc+wfp5JzByUyKwtaMRK70iAgtFluvr8tVwRA6u4ec0NkO+bDen63ZYrG6vTaSBkkHHwDMjIvAWzlz0dzaicLzdTDV30WLxYpwtQrJ0WFYO4e7LhDRD/qaBVaOioXgsMN665qr3dl14x9QRfW+jy9ngf2X5IPPKTI0CK8u5t6bRNS/e7PADT3anfJhaoQkzYf5RB4in/ondN24jPbqUxiX83qPc3AW2L9J8uYWIqKhWpsS1+d7o5ZvhWDrQt3/3YSmg68jcvnWbqMMTgKAtXP6Pg/5Np9Z8RERDcTo0CCkT47qdRZYERyGMWv+e7+fl8nu3TfASyj+iys+IvI7nAWm/jD4iMjvcBaY+sNWJxH5Jc4CU18kuVcnEZG7XKgzcxaYumHwEVFA4CwwOTH4iIgooPDmFiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCigMPiIiCij/H2J3WADiTRbGAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "\n", + "max_cut_graph_edges = [(0,1), (1,2), (1,3), (3,4), (4,5), (4,6)]\n", + "n_nodes = 7\n", + "\n", + "max_cut_graph = nx.Graph()\n", + "max_cut_graph.add_edges_from(max_cut_graph_edges)\n", + "nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()})\n", + "\n", + "expected_results = [(0,1,0,0,1,0,0), (1,0,1,1,0,1,1)]" + ] + }, + { + "cell_type": "markdown", + "id": "18a5bd16", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define Cost Hamiltonian: $\\gamma H$" + ] + }, + { + "cell_type": "markdown", + "id": "543f87ca", + "metadata": {}, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "99226b24", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(): 3.00000000000000, (Zq[0], Zq[1]): -0.500000000000000, (Zq[1], Zq[2]): -0.500000000000000, (Zq[1], Zq[3]): -0.500000000000000, (Zq[3], Zq[4]): -0.500000000000000, (Zq[4], Zq[5]): -0.500000000000000, (Zq[4], Zq[6]): -0.500000000000000}\n" + ] + } + ], + "source": [ + "from typing import List, Tuple, Any\n", + "from pytket.utils import QubitPauliOperator\n", + "from pytket.pauli import QubitPauliString, Pauli\n", + "from pytket import Qubit\n", + "\n", + "def qaoa_graph_to_cost_hamiltonian(edges: List[Tuple[int, int]], cost_angle: float) -> QubitPauliOperator:\n", + " qpo_dict = {QubitPauliString(): len(edges)*0.5*cost_angle}\n", + " for e in edges:\n", + " term_string = QubitPauliString([Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z])\n", + " qpo_dict[term_string] = -0.5*cost_angle\n", + " return QubitPauliOperator(qpo_dict)\n", + "\n", + "cost_angle = 1.0\n", + "cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle)\n", + "print(cost_ham_qpo)" + ] + }, + { + "cell_type": "markdown", + "id": "6da499ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = 3 I^{\\otimes 6} -0.5 \\big[ Z_0 Z_1 + Z_1 Z_2 +Z_1 Z_3 +Z_3 Z_4 +Z_4 Z_5 +Z_4 Z_6 \\big]\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Using the same index convention as above" + ] + }, + { + "cell_type": "markdown", + "id": "785ff56c", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Hamiltonian Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "11fe9917", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.utils import gen_term_sequence_circuit\n", + "from pytket import Circuit\n", + "from pytket.circuit import display\n", + "\n", + "cost_ham_circuit = gen_term_sequence_circuit(cost_ham_qpo, Circuit(n_nodes))\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9057c55f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.transform import Transform\n", + "\n", + "Transform.DecomposeBoxes().apply(cost_ham_circuit)\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4690b787", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construction of the Mixer Hamiltonian: $\\beta B$" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "296c560d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mixer_angle = 0.8\n", + "mixer_ham_qpo = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer_angle for i in range(n_nodes)})\n", + "mixer_ham_circuit = gen_term_sequence_circuit(mixer_ham_qpo, Circuit(n_nodes))\n", + "Transform.DecomposeBoxes().apply(mixer_ham_circuit)\n", + "display.render_circuit_jupyter(mixer_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4d128a70", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define the Initial State" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "0a9db628", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pytket.backends.backend import Backend\n", + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "def qaoa_initial_circuit(n_qubits: int) -> Circuit:\n", + " c = Circuit(n_qubits)\n", + " for i in range(n_qubits):\n", + " c.H(i)\n", + " return c\n", + "\n", + "superposition_circuit = qaoa_initial_circuit(n_nodes)\n", + "\n", + "display.render_circuit_jupyter(superposition_circuit)\n", + "\n", + "Transform.DecomposeBoxes().apply(superposition_circuit)\n", + "backend.default_compilation_pass(2).apply(superposition_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "da759b59", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construct QAOA Circuit" + ] + }, + { + "cell_type": "markdown", + "id": "359a1a0f-e92e-40ae-bbe6-ce960b118f49", + "metadata": {}, + "source": [ + "Now lets define a function to create our entire QAOA circuit. For $p$ QAOA layers we expect that our circuit will require $2p$ parameters. Here we will pass and cost mixer parameters in as a list where the length of the list defines the number of layers." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "5e97191f-9d15-4f8a-bc2a-70ea60e78553", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1.4 s, sys: 1e+03 ns, total: 1.4 s\n", + "Wall time: 1.4 s\n" + ] + } + ], + "source": [ + "%%time\n", + "cost_circuits = []\n", + "mixer_circuits = []\n", + "\n", + "for i in range(10):\n", + " cost_circuit = gen_term_sequence_circuit(qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, 1/(2**(i + 1))), Circuit(n_nodes))\n", + " mixer_circuit = gen_term_sequence_circuit(QubitPauliOperator({QubitPauliString([Qubit(j)], [Pauli.X]): 1/(2**(i + 1)) for j in range(n_nodes)}), Circuit(n_nodes))\n", + " Transform.DecomposeBoxes().apply(cost_circuit)\n", + " Transform.DecomposeBoxes().apply(mixer_circuit)\n", + " backend.default_compilation_pass(2).apply(cost_circuit)\n", + " backend.default_compilation_pass(2).apply(mixer_circuit)\n", + " cost_circuits.append(cost_circuit)\n", + " mixer_circuits.append(mixer_circuit)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "23f8910a", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_max_cut_circuit(edges: List[Tuple[int, int]],\n", + " n_nodes: int,\n", + " mixer_angles: List[int],\n", + " cost_angles: List[int]) -> Circuit:\n", + " \n", + " assert len(mixer_angles) == len(cost_angles)\n", + " \n", + " # initial state\n", + " qaoa_circuit = superposition_circuit.copy()\n", + " \n", + " \n", + " \n", + " # add cost and mixer terms to state\n", + " for cost, mixer in zip(cost_angles, mixer_angles):\n", + " for i in range(10):\n", + " if (cost & (1 << i)):\n", + " qaoa_circuit.append(cost_circuits[9 - i])\n", + " for i in range(10):\n", + " if (mixer & (1 << i)):\n", + " qaoa_circuit.append(mixer_circuits[9 - i])\n", + " \n", + " print(qaoa_circuit)\n", + " \n", + " return qaoa_circuit" + ] + }, + { + "cell_type": "markdown", + "id": "bc2f8939-41b7-476b-a5a8-09de07211079", + "metadata": {}, + "source": [ + "We also need to extract our energy expectation values from a `BackendResult` object after our circuit is processed by the device/simulator. We do this with the `get_max_cut_energy` function below. Note that the fact that the maxcut Hamiltonian contains only commuting terms means that we do not need to calculate our energy expectation using multiple measurement circuits. This may not the the case for a different problem Hamiltonian." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "df387eea-4198-428e-9b92-4f3bceb12f0e", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple\n", + "from pytket.backends.backendresult import BackendResult\n", + "\n", + "def get_max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult) -> float:\n", + " energy = 0.0\n", + " dist = results.get_distribution()\n", + " for i, j in edges:\n", + " energy += sum((meas[i] ^ meas[j]) * prob for meas, prob in dist.items())\n", + "\n", + " return energy" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "e5abad7b-e989-4156-9708-3d8c97d8ca2a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "from typing import Callable\n", + "import numpy as np\n", + "\n", + "def qaoa_instance(\n", + " backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " guess_mixer_angles: np.array,\n", + " guess_cost_angles: np.array,\n", + " seed: int,\n", + " shots: int = 5000,\n", + ") -> float:\n", + " \n", + " print(\"test1\")\n", + " # step 1: get state guess\n", + " my_prep_circuit = qaoa_max_cut_circuit(\n", + " max_cut_graph_edges, n_nodes, guess_mixer_angles, guess_cost_angles\n", + " )\n", + " print(\"test2\")\n", + " measured_circ = my_prep_circuit.copy().measure_all()\n", + " res = backend.run_circuit(measured_circ, shots, seed=seed)\n", + " print(\"test3\")\n", + "\n", + " return get_max_cut_energy(max_cut_graph_edges, res)" + ] + }, + { + "cell_type": "markdown", + "id": "2c01c28b", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Optimise Energy by Guessing Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "0a44bed8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_optimise_energy(compiler_pass: Callable[[Circuit], bool],\n", + " backend: Backend,\n", + " iterations: int = 100,\n", + " n: int = 3,\n", + " shots: int = 5000,\n", + " seed: int= 12345):\n", + " \n", + " highest_energy = 0 \n", + " best_guess_mixer_angles = [0 for i in range(n)] \n", + " best_guess_cost_angles = [0 for i in range(n)]\n", + " rng = np.random.default_rng(seed)\n", + " # guess some angles (iterations)-times and try if they are better than the best angles found before\n", + " \n", + " for i in range(iterations):\n", + " \n", + " guess_mixer_angles = rng.integers(0, 1024, n)\n", + " guess_cost_angles = rng.integers(0, 1024, n)\n", + " \n", + " qaoa_energy = qaoa_instance(backend,\n", + " compiler_pass,\n", + " guess_mixer_angles,\n", + " guess_cost_angles,\n", + " seed=seed,\n", + " shots=shots)\n", + " \n", + " if(qaoa_energy > highest_energy):\n", + " \n", + " print(\"new highest energy found: \", qaoa_energy)\n", + " \n", + " best_guess_mixer_angles = np.round(guess_mixer_angles, 3)\n", + " best_guess_cost_angles = np.round(guess_cost_angles, 3)\n", + " highest_energy = qaoa_energy\n", + " print(\"iteration: \", i)\n", + " \n", + " print(\"highest energy: \", highest_energy)\n", + " print(\"best guess mixer angles: \", best_guess_mixer_angles)\n", + " print(\"best guess cost angles: \", best_guess_cost_angles)\n", + " return best_guess_mixer_angles, best_guess_cost_angles" + ] + }, + { + "cell_type": "markdown", + "id": "d22226cc", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Calculate the State for the final Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "da46e63d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_calculate(backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " shots: int = 5000,\n", + " iterations: int = 100,\n", + " seed: int = 12345,\n", + " ) -> BackendResult:\n", + " \n", + " # find the parameters for the highest energy\n", + " best_mixer, best_cost = qaoa_optimise_energy(compiler_pass,\n", + " backend,\n", + " iterations,\n", + " 3,\n", + " shots=shots,\n", + " seed=seed)\n", + " \n", + " # get the circuit with the final parameters of the optimisation:\n", + " my_qaoa_circuit = qaoa_max_cut_circuit(max_cut_graph_edges,\n", + " n_nodes,\n", + " best_mixer,\n", + " best_cost)\n", + "\n", + " my_qaoa_circuit.measure_all()\n", + "\n", + " compiler_pass(my_qaoa_circuit)\n", + " handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed)\n", + "\n", + " result = backend.get_result(handle) \n", + " \n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "9dd97e10", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Results with the Noiseless Simulator" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "e7afb38e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "aaea7e2f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 1.8900000000000001\n", + "iteration: 0\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 2.1818\n", + "iteration: 1\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 3.0740000000000007\n", + "iteration: 2\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 3\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 4\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 3.314199999999999\n", + "iteration: 5\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 6\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 7\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 4.2726\n", + "iteration: 8\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 9\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 10\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 11\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 12\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 13\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 14\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 15\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 16\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 17\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 18\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 19\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 20\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 21\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 22\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 23\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 24\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 25\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 26\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 27\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 28\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 29\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 30\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 31\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 32\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 33\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 34\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 35\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 36\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 37\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 38\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 39\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 40\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 4.7476\n", + "iteration: 41\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 42\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 43\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 44\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 45\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 46\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 47\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 48\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 49\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 50\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 51\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 52\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 53\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 54\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 55\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 56\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "new highest energy found: 4.8962\n", + "iteration: 57\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 58\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 59\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 60\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 61\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 62\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 63\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 64\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 65\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 66\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 67\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 68\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 69\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 70\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 71\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 72\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 73\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 74\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 75\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 76\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 77\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 78\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 79\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 80\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 81\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 82\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 83\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 84\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 85\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 86\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 87\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 88\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 89\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 90\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 91\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 92\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 93\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 94\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 95\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 96\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 97\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 98\n", + "test1\n", + "\n", + "test2\n", + "test3\n", + "iteration: 99\n", + "highest energy: 4.8962\n", + "best guess mixer angles: [293 132 142]\n", + "best guess cost angles: [401 814 329]\n", + "\n", + "CPU times: user 13.7 s, sys: 12.1 ms, total: 13.7 s\n", + "Wall time: 13.6 s\n" + ] + } + ], + "source": [ + "%%time\n", + "res = qaoa_calculate(backend, backend.default_compilation_pass(2).apply, shots = 5000, iterations = 100, seed=14839)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "3b86301e-7645-4553-be38-3ebf89eedd37", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success ratio 0.3734 \n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from maxcut_plotting import plot_maxcut_results\n", + "\n", + "plot_maxcut_results(res, 6)" + ] + }, + { + "cell_type": "markdown", + "id": "6e36c4fb-a118-4ab8-be01-77a674f273e3", + "metadata": {}, + "source": [ + "Here the binary strings in the results correspond to the two optimal colourings of our graph." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "ffce2a97-902c-44d8-8d64-b25498752907", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "G = nx.Graph()\n", + "G.add_edges_from(max_cut_graph_edges)\n", + "\n", + "H = nx.Graph()\n", + "H.add_edges_from(max_cut_graph_edges)\n", + "\n", + "plt.figure(1)\n", + "nx.draw(G, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['red', 'blue', 'red','red', 'blue', 'red', 'red'])\n", + "plt.figure(2)\n", + "nx.draw(H, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['blue', 'red', 'blue', 'blue', 'red', 'blue', 'blue'])\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c4f545a-011b-44b5-943d-c520f4cb3483", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "vscode": { + "interpreter": { + "hash": "3289aa74b4cc5b65254d7b081e6c83acb4efa1b1c1d2fe845644451ee4b44b02" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/team_solutions/pineapple/maxcut_notebook.ipynb b/team_solutions/pineapple/maxcut_notebook.ipynb new file mode 100644 index 0000000..2f68c70 --- /dev/null +++ b/team_solutions/pineapple/maxcut_notebook.ipynb @@ -0,0 +1,1081 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3ba3449", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The Quantum Approximate Optimisation Algorithm (QAOA) using TKET.\n", + "\n", + "Callum Macpherson" + ] + }, + { + "cell_type": "markdown", + "id": "45668632", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## The Max-Cut problem" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9456fbeb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAJ4AAACeCAYAAADDhbN7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAAsTAAALEwEAmpwYAAASvUlEQVR4nO2de1BV5frHP2wU3CKmCASoKIiKIioCHjV/apczZoBiOJpadvN0OZnVyROVaY4np3HmaI2eIm8n7YyDOuQFGLOjaWkKySW5lKIpighqGonIRdh7nT92WPunxb6/ay/ez4x/sdZ6vujX513Pep/3fT0URVGQSFyMTrQASftEGk8iBGk8iRCk8SRCkMaTCEEaTyIEaTyJEKTxJEKQxpMIwX7jFRfDk0/CwIEQEgJhYTBuHGzdCjdvOkCiRIt42Dxl9vnn8Npr8MMP0NQEBoP5z319QaeDF16AxYvB29sBciVawTbjvfceLFwIDQ1tX6vXQ1QU7N0L3bpZr1CiSaw33rp18PLLUF9v+T1eXjBsGBw6JDOfBLD2Ha+8HObPt850YHrXKy2Ff/zDuvskmsU6461eDUajbZEaGuBf/4LmZtvul2gKy43X2Ajr19tXqRqNsHOn7fdLNIPlxvv8c/ujXb9uynqSdo/lxrtwwTHDZGWl/c+QuD2WG6+h4fZvdbbQ1GT/MyRuj+XG69YNOna0O2CNwUBRUREtLS12P0vivnSw+MrYWLuDGXQ6inx8eG7GDCorKxk+fDjx8fG3/kRERODh4WF3HIn6se4D8pAh8N13tkfT6yEvD6KiuHbtGgUFBeTl5ZGXl8fRo0e5fv26mRHj4+Pp2bOn7fEkqsU64/3nP/DXv0JdnW3RRoyAgoLf/fGlS5duGbHVjF5eXsTHxzNy5Eji4+OJi4vDz8/PtvgS1WCd8RobITLSVJlaW2jo9ZCZCQ88YPEtiqJw9uxZMzMWFhYSGBh4KyOOHDmSmJgYfHx8rNMjEYr1c7Xnzpne937+2XLzde4My5fDvHnWK/x/GAwGTpw4YWbG0tJSIiIizMwYHR1NRwcUQxLnYFt3yrlzMH48XL36x8Out7epNerDD+GJJ2xX2QZNTU2UlJRw9OjRW2YsLy8nOjra7H1x4MCB6HSy91UN2N6P19QEGRmmTHb6NHh4mD4we3pi0Om4UV+P74IFeMybB6GhDpbdNnV1dRQWFt56V8zLy+Pq1avExsaamTE0NFRW0gKw3Xi/pagIvv3WNPzq9dCzJ8Nfe420DRsYPXq0/SodxJUrV8jPzzcrXhRFua2SDggIEC1V8zjGeHdg8eLF3LhxgxUrVjjj8Q5BURQqKyvN3hfz8/Pp3r27mRFjY2Px9fUVLVdTOM14xcXFTJ48mfLycrcayoxGI6dOnTIzY1FREX369Ln1SSc+Pp5hw4bhLZtabcZpxlMUhQEDBpCenk5cXJwzQriM5uZmSktLzcx48uRJBg8ebPaNcdCgQXh6eoqW6xY4zXgAb7zxBgDvvvuus0IIo76+nmPHjplV0tXV1cTExJh91gkLC3OrjO8qnGq8/Px8Zs6cycmTJ9vFX35NTc2tacBWQzY2NhIXF2f2zhgcHCxaqnCcajxFUQgLCyMzM5OhQ4c6K4yqqa6uNqui8/Pz6dy5s5kR4+Li6KbSFXiKAjU1cO0adOoEPXqY1m7Zi1ONB/Dqq6/i4+PD0qVLnRnGbVAUhTNnzpi9L3777beEhISYmTEmJga9Xi9M55UrsGGDaSVrTY2pI85oNE1WJSfDq6/CyJG2P9/pxsvJyWHu3Ll8Z09Xi8ZpaWnh+PHjZmY8fvw4AwYMMDNjVFSU06cBDQbT6tV160yTTndaOq3TmbJf376mJTT9+1sfx+nGMxqN9O7dm3379jFo0CBnhtIUjY2NFBUVmZmxoqKCYcOG3dbD6KhpwOZmSEiAw4ctW8Gq00GXLnDggKnxyBqcbjyA+fPnExgYyFtvveXsUJqmtraWwsJCs0r62rVrtxUvPXv2tLqYUxSYNcvUQGTtsunu3U0TV336WH6PS4z31Vdf8dJLL3Hs2DFnh2p3XL58+dY0YKshPT09b+th7NGjxx8+JycH/vxnuHHDeg06HaSkwLZtlt/jEuMZDAZCQkI4fPgwERERzg7XrlEUhYqKCrNKurCwEH9/f7PviyNGjDDrYUxJgR07TJnPFjp1MrVptuHvW7jEeADPPfccYWFhpKamuiKc5DcYjUbKysrM3hdLSkoIDw8nPj6eyMj/Y9Gix7l50/Z3Rb0eliwxbSBmCS4z3r59+3jzzTc5evSoK8JJ2uDmzZuUlJSQl5fHli06Dh2ajdFoXxf30KGmRiVLcJnxmpubCQ4OpqCggD7WvIVKnM7q1fD3v9u/5LlXLzh/3rJrXdaO27FjR6ZMmcL27dtdFVKiYlzaB56SkkJGRoYrQ0oswM/PIWv16d7d8mtdarz777+f48ePc+HCBVeGlbTB/ffbvy2OXg8zZlh+vUuN5+3tTUJCAjt27HBlWEkbBAXBxImmZTO2oijwl79Yfr3Ll1xNmzaNTz/91NVhJW2wYIFpFaot6HQwaRIEBlp+j8uq2lYaGhoICgri1KlTBFqjVOJUFMX0EXnPHoWGButS3113QWEhhIdbfo/LM55er2fSpEnslDuDqgoPD0hPh6Cg83h4WLCb/y/3+PrCf/9rnelA0Mk+srpVJ1lZGdy8OZ6ZM414e5umwe6Eh4epKyU8HL75xsa+PEUA169fV3x9fZUrV66ICC+5A998843i7++vFBYWKoqiKNXVirJkiaL4+SmKXq8ovr6K4uOjKF5eipKUpCgHDyqK0Wh7PJe/47Xy8MMPk5SUxJNPPikivOQ3VFRUMHr0aNLS0pg8ebLZzwwGuHz517X6AQHgiP2RhG0kIqtbdVBbW0tiYiILFiy4zXQAnp4QHAyDBpk6jh21KZewjFdbW0uvXr04f/48d911lwgJ7Z6WlhYmT55MaGgoaWlpLl0JKCzjde3alfHjx5OdnS1KQrvnlVdeoaWlhdWrV7t8+anQPbtkdSuO1atXs3//frZt2yZkH0FhQy3ATz/9RN++famqqqJLly6iZLQ7du/ezdy5czl8+DBhYWFCNAjNeH5+fowePZrdu3eLlNGuKC4u5vHHH+fTTz8VZjpQwdHwsrp1HRcvXiQpKYlVq1YJ37dQ6FAL8OOPPxIREcHFixeFrpzXOvX19UyYMIGkpCQWLVokWo74jBcQEEBsbCyfO+KQPskdMRqNzJkzh4EDB6pmbbNw44Gsbp3NwoULuXTpEuvXr1fNrl3Ch1qAqqoqoqKiuHjxotxl08F8/PHHLFu2jNzcXPz9/UXLuYUqMl5ISAhRUVHs27dPtBRNceDAAV5//XWys7NVZTpQifFAVreOpqysjEceeYT09HQiIyNFy7kNVQy1YOqQiImJ4eLFi/JEHju5evUqo0aNIjU1lblz54qWc0dUk/FCQ0OJiIjgyy+/FC3FrWlqamLq1Kk8/PDDqjUdqMh4IKtbe1EUhWeeeYaAgADVb3iumqEW4PTp04wZM4aqqiq5bb8NLFu2jB07dnDw4EE627pkzEWoKuP169ePkJAQDh06JFqK27Ft2zbWrFlDZmam6k0HKjMeyOrWFnJzc5k3bx5ZWVmEhISIlmMRqhpqAU6cOMF9991HZWWlPOLTAs6ePcuYMWNYt24dCQkJouVYjOr+ZSMjI/Hz8yM3N1e0FNVz7do1EhMTSU1NdSvTgQqNB7K6tYSWlhZmzJjB+PHjmT9/vmg5VqO6oRagpKSExMREzp49q5pJbTWhKAovvPAC5eXlZGVl0aFDB9GSrEaVGW/IkCF4e3uTn58vWooqWbVqFQcPHmTLli1uaTpQqfE8PDxkdfs7ZGdns3z5crKzs917WajtmxA4l/z8fKVfv36K0Z59EjTGsWPHFH9/fyUnJ0e0FLtRZcYDGDFiBAaDgeLiYtFSVEFVVRVJSUl88MEHjBo1SrQcu1Gt8Tw8PGR1+ws3btxg8uTJPPvss0yfPl20HIegyqq2ldzcXJ566im+//570VKEYTQaSUlJoWvXrmzcuFEzVb5qMx7AyJEjqa2tbdfGe+ONN/jpp59Yu3atZkwHKjeeTqcjJSWl3Va369evZ/v27Wzfvl17a1FEVzdt8dVXXylDhw4VLcPl7Nu3TwkMDFTKyspES3EKqs54APfccw+XLl3i1KlToqW4jBMnTjBz5ky2bt3KgAEDRMtxCqo3nqenJ1OnTm03w+2VK1dITExk+fLlTJgwQbQcp6F640H76dFramoiOTmZ6dOna36LXlV/TmmlpaWFoKAg8vPz6du3r2g5TkFRFObMmUNjYyNbt27VfC+iW/x2HTp0IDk5WdMnP77zzjuUlZWxadMmzZsO3MR4oO0evfT0dDZs2OA26yUcgVsMtWA6UTooKIiSkhJ69uwpWo7DOHLkCMnJyXzxxRdER0eLluMy3CbjeXl5kZiYqKnhtry8nJSUFDZt2tSuTAduZDzQVnX7888/k5CQwMKFC5k0aZJoOS7HbYZagMbGRoKCgigrK+Puu+8WLcdmmpubSUhIIDIyklWrVomWIwS3ynidOnVy+5MfFUXhxRdfpEOHDqxcuVK0HGG4lfHA/avb9957jyNHjrj1eglH4FZDLZiaIkNCQjhz5gw9evQQLccqMjMzef7558nJySE0NFS0HKG4Xcbz8fHhgQceYNeuXaKlWEVhYSFPP/00O3fubPemAzc0HrhfdXvhwgWmTJlCWloa8fHxouWoArcbauHXkx8rKiro1q2baDl/SF1dHePGjWP69Om8/vrrouWoBrfMeF27dmXChAmqP/nRYDAwe/Zshg8fTmpqqmg5qsItjQfuUd2mpqZSW1vLRx99pKn1Eo7ALYdagJqaGvr06cOFCxfw9fUVLec21qxZw8qVK8nJycHPz0+0HNXhthmve/fujBkzRpUnP+7du5e3336b7OxsabrfwW2NB+qsbr///ntmz57Ntm3b6N+/v2g5qsVth1r49eTH6upqVfSxXb58mVGjRrFkyRLmzJkjWo6qceuMFxAQQFxcnCpOfmxsbCQ5OZlZs2ZJ01mAWxsP1FHdKorCU089Re/evVm6dKlQLe6CWw+1ANXV1QwePFjoyY9Llixhz549HDhwQB72bCFun/GCg4MZMmQIe/fuFRJ/8+bNbNq0iV27dknTWYHbGw/EVbdff/01r7zyCllZWW7dmCoCtx9qAc6fP8/w4cNdevLj6dOnGTt2LBs3bmTixIkuiaklNJHxevfuTf/+/Tlw4IBL4tXU1JCYmMjixYul6WxEE8YD11W3zc3NTJs2jYkTJ/L88887PZ5W0cRQC3DmzBlGjRpFVVWV01rKlV+O5ayurmbXrl3yhEk70EzGCw8Pp1evXk49+XHFihXk5eWRnp4uTWcnmjEeOLe63bFjB++//z5ZWVmq7IZxNzQz1AKUlZVx7733Ovzkx4KCAh588EH27NlDbGysw57bntFUxhs4cCA9evQgJyfHYc+srKxkypQprF27VprOgWjKeODY6rauro7ExEReeuklpk6d6pBnSkxoaqgFKC0t5aGHHuLcuXN2tZsbDAaSk5MJCgrS3Fb/akBzGS8qKgq9Xk9eXp5dz1mwYAH19fV8+OGH0nROQHPGc8TJj2lpaXz22WdkZGS4bAqu3eGaUw1cS0FBgRIeHm7TyY979uxR7r77buWHH35wgjJJK5rLeAAxMTEoikJRUZFV95WWlvLYY4+RkZFBv379nKROAhocasG2kx8vXbpEUlISK1euZOzYsU5UJwGNGg9MsxgZGRkoFhTtDQ0NJCcnM2fOHB599FEXqJNo7nNKK0aDgRnBwWyIjqbr+fNw4wZ07gyDB8PLL8OECeDhgdFoZNasWeh0OjZv3iwrWBehPeMpCvz737B0KY3V1Xg1N9+e1rt0gW7dYNEiFlVU8MX+/ezfv59OnToJENw+0ZbxDAZ45hnYsgXq69u8vNnLi6yOHRl76hSBwcEuEChpRVvveC++aLHpADrevEmywUDgW2+ZMqXEZWjHeJ99Bp98YrHpWtE1NsLWrZCZ6SRhkjuhnaF23Diwpwn0T3+C3FzH6ZH8IdrIeKdPg51zsxQXQ1mZY/RI2kQbxvvkE1NhYQ/NzbBxo0PkSNpGG8Y7fdpkHHtoaYEzZxyjR9Im2jCelQXF71JX55jnSNpEG8Zz1EEr/v6OeY6kTbRhvLFjTbMR9uDjA/fc4xg9kjbRxueUhgYIDLRvqOzcGS5fNhlQ4nS0kfH0enjiCbC1W7hDB3jsMWk6F6KNjAdw7hxER8P169bf26ULFBVBeLjjdUnuiDYyHkCfPpCdbRoyraFzZ9i5U5rOxWgn47Vy5AhMmmT6LvdHn1n0etMQm51tmm6TuBTtGQ+gpgY+/hj++U/T0Gs0mj4wd+wIOp1paP3b3+Dpp0EegCIEbRqvFaMRDh6EkydNBvT1hYgIU/exA/dWkViPto0nUS3yv71ECNJ4EiFI40mEII0nEYI0nkQI0ngSIUjjSYQgjScRgjSeRAj/A8813h4bT+gEAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "G = nx.Graph()\n", + "G.add_edges_from([(0,1), (1,2), (2,0)])\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, node_color=['red', 'blue', 'red'])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cad41481", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "There are \\\\(2^3\\\\) possible assignments of colour to nodes. In general there are \\\\( 2^n \\\\). The Max-cut problem can then be stated as that of finding the colour assignment which maximises the number of edges between vertices of a different colour." + ] + }, + { + "cell_type": "markdown", + "id": "7389bbe5", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Quantum Approximate Optimization Algorithm (QAOA)\n", + "\n", + "Introduced in 'A Quantum Approximate Optimization Algorithm' (found at https://arxiv.org/abs/1411.4028). The idea is to prepare a quantum state which encodes a solution to the Max-cut problem.\n", + "\n", + "\n", + "This is a variational algorithm, which is to say that a paramaterised state is prepared, with the parameters varied to improve the solution. We will have $2p$ parameters where p is our number of layers. In particular, the state prepared has the form \n", + "\n", + "\n", + "\n", + "\\\\[ \\left| \\psi \\left( \\beta, \\gamma \\right) \\right\\rangle = U \\left( \\beta_m \\right) U \\left( \\gamma_m \\right) ... U \\left( \\beta_0 \\right) U \\left( \\gamma_0 \\right) \\left| \\psi_0 \\right\\rangle \\\\]\n", + "where\n", + "\\\\[ U \\left( \\beta_i \\right) = e^{i \\beta H_B} \\quad \\& \\quad U \\left( \\gamma_i \\right) = e^{i \\gamma H_P} \\\\]\n", + "with \\\\( H_B \\\\) and \\\\( H_P \\\\) depending on the problem instance. " + ] + }, + { + "cell_type": "markdown", + "id": "3596e66d", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "d720b387", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "For the previous 3 vertex graph the *problem Hamiltonian* is\n", + "\\\\[ H_P = \\frac{1}{2} \\big[ \\left( Z \\otimes Z \\otimes I \\right) + \\left( Z \\otimes I \\otimes Z \\right) + \\left( I \\otimes Z \\otimes Z \\right) \\big] \\\\]\n", + "\n", + "\n", + "where you will notice that there is a \\\\( Z \\otimes Z \\\\) acting between each vertex which is connected by an edge." + ] + }, + { + "cell_type": "markdown", + "id": "8ca2e1b9", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "The *mixer Hamiltonian* has the form \n", + "\\\\[ H_B = \\left( X \\otimes I \\otimes I \\right) + \\left( I \\otimes X \\otimes I \\right) + \\left( I \\otimes I \\otimes X \\right) \\\\]\n", + "\n", + "\n", + "where you will notice that there is an \\\\( X \\\\) acting on each vertex." + ] + }, + { + "cell_type": "markdown", + "id": "de6b9e03", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "\n", + "A solution to maxcut can be found by maximising the following cost function $C$ .\n", + "\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here $x_i$ and $x_j$ are the the \"colours\" of each vertex. \n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i,x_j \\in \\{0,1\\}\n", + "\\end{equation}\n", + "$$\n", + "\n", + "$x_i(1-x_j)=0$ if $x_i=x_j$ and $ x_i(1-x_j)=1$ if the terms are not equal." + ] + }, + { + "cell_type": "markdown", + "id": "61d4e798", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We want to encode our Maxcut cost function as a Hamiltonain. To do this we can perform the following translation.\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i \\mapsto \\frac{1}{2}(1-Z_i)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "The Pauli Z operator can be used to distinguish between the $|0\\rangle$ and $|1\\rangle$ basis states as these are eigenstates with eigenvalues $\\pm 1$ .\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_B = \\sum_i X_i\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here we use the the convention that $X_i$ means a Pauli X operator will be applied to the \"ith\" qubit and the identity operator will be applied to all other qubits in the circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "aeb6abd9", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "id": "3cbb783a", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Circuit Construction for QAOA" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "688f1332", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "\n", + "max_cut_graph_edges = [(0,1), (1,2), (1,3), (3,4), (4,5), (4,6)]\n", + "n_nodes = 7\n", + "\n", + "max_cut_graph = nx.Graph()\n", + "max_cut_graph.add_edges_from(max_cut_graph_edges)\n", + "nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()})\n", + "\n", + "expected_results = [(0,1,0,0,1,0,0), (1,0,1,1,0,1,1)]" + ] + }, + { + "cell_type": "markdown", + "id": "18a5bd16", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define Cost Hamiltonian: $\\gamma H$" + ] + }, + { + "cell_type": "markdown", + "id": "543f87ca", + "metadata": {}, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "99226b24", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(): 3.00000000000000, (Zq[0], Zq[1]): -0.500000000000000, (Zq[1], Zq[2]): -0.500000000000000, (Zq[1], Zq[3]): -0.500000000000000, (Zq[3], Zq[4]): -0.500000000000000, (Zq[4], Zq[5]): -0.500000000000000, (Zq[4], Zq[6]): -0.500000000000000}\n" + ] + } + ], + "source": [ + "from typing import List, Tuple, Any\n", + "from pytket.utils import QubitPauliOperator\n", + "from pytket.pauli import QubitPauliString, Pauli\n", + "from pytket import Qubit\n", + "\n", + "def qaoa_graph_to_cost_hamiltonian(edges: List[Tuple[int, int]], cost_angle: float) -> QubitPauliOperator:\n", + " qpo_dict = {QubitPauliString(): len(edges)*0.5*cost_angle}\n", + " for e in edges:\n", + " term_string = QubitPauliString([Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z])\n", + " qpo_dict[term_string] = -0.5*cost_angle\n", + " return QubitPauliOperator(qpo_dict)\n", + "\n", + "cost_angle = 1.0\n", + "cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle)\n", + "print(cost_ham_qpo)" + ] + }, + { + "cell_type": "markdown", + "id": "6da499ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = 3 I^{\\otimes 6} -0.5 \\big[ Z_0 Z_1 + Z_1 Z_2 +Z_1 Z_3 +Z_3 Z_4 +Z_4 Z_5 +Z_4 Z_6 \\big]\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Using the same index convention as above" + ] + }, + { + "cell_type": "markdown", + "id": "785ff56c", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Hamiltonian Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "11fe9917", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.utils import gen_term_sequence_circuit\n", + "from pytket import Circuit\n", + "from pytket.circuit import display\n", + "\n", + "cost_ham_circuit = gen_term_sequence_circuit(cost_ham_qpo, Circuit(n_nodes))\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9057c55f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.transform import Transform\n", + "\n", + "Transform.DecomposeBoxes().apply(cost_ham_circuit)\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4690b787", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construction of the Mixer Hamiltonian: $\\beta B$" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "296c560d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mixer_angle = 0.8\n", + "mixer_ham_qpo = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer_angle for i in range(n_nodes)})\n", + "mixer_ham_circuit = gen_term_sequence_circuit(mixer_ham_qpo, Circuit(n_nodes))\n", + "Transform.DecomposeBoxes().apply(mixer_ham_circuit)\n", + "display.render_circuit_jupyter(mixer_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4d128a70", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define the Initial State" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0a9db628", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def qaoa_initial_circuit(n_qubits: int) -> Circuit:\n", + " c = Circuit(n_qubits)\n", + " for i in range(n_qubits):\n", + " c.H(i)\n", + " return c\n", + "\n", + "superposition_circuit = qaoa_initial_circuit(n_nodes)\n", + "\n", + "display.render_circuit_jupyter(superposition_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "da759b59", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construct QAOA Circuit" + ] + }, + { + "cell_type": "markdown", + "id": "359a1a0f-e92e-40ae-bbe6-ce960b118f49", + "metadata": {}, + "source": [ + "Now lets define a function to create our entire QAOA circuit. For $p$ QAOA layers we expect that our circuit will require $2p$ parameters. Here we will pass and cost mixer parameters in as a list where the length of the list defines the number of layers." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "23f8910a", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_max_cut_circuit(edges: List[Tuple[int, int]],\n", + " n_nodes: int,\n", + " mixer_angles: List[float],\n", + " cost_angles: List[float]) -> Circuit:\n", + " \n", + " assert len(mixer_angles) == len(cost_angles)\n", + " \n", + " # initial state\n", + " qaoa_circuit = qaoa_initial_circuit(n_nodes)\n", + " \n", + " # add cost and mixer terms to state\n", + " for cost, mixer in zip(cost_angles, mixer_angles):\n", + " cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost)\n", + " mixer_ham = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer for i in range(n_nodes)})\n", + " qaoa_circuit.append(gen_term_sequence_circuit(cost_ham, Circuit(n_nodes)))\n", + " qaoa_circuit.append(gen_term_sequence_circuit(mixer_ham, Circuit(n_nodes)))\n", + " \n", + " Transform.DecomposeBoxes().apply(qaoa_circuit)\n", + " return qaoa_circuit" + ] + }, + { + "cell_type": "markdown", + "id": "bc2f8939-41b7-476b-a5a8-09de07211079", + "metadata": {}, + "source": [ + "We also need to extract our energy expectation values from a `BackendResult` object after our circuit is processed by the device/simulator. We do this with the `get_max_cut_energy` function below. Note that the fact that the maxcut Hamiltonian contains only commuting terms means that we do not need to calculate our energy expectation using multiple measurement circuits. This may not the the case for a different problem Hamiltonian." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "df387eea-4198-428e-9b92-4f3bceb12f0e", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple\n", + "from pytket.backends.backendresult import BackendResult\n", + "\n", + "def get_max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult) -> float:\n", + " energy = 0.0\n", + " dist = results.get_distribution()\n", + " for i, j in edges:\n", + " energy += sum((meas[i] ^ meas[j]) * prob for meas, prob in dist.items())\n", + "\n", + " return energy" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "e5abad7b-e989-4156-9708-3d8c97d8ca2a", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket.backends.backend import Backend\n", + "from typing import Callable\n", + "import numpy as np\n", + "\n", + "def qaoa_instance(\n", + " backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " guess_mixer_angles: np.array,\n", + " guess_cost_angles: np.array,\n", + " seed: int,\n", + " shots: int = 5000,\n", + ") -> float:\n", + " # step 1: get state guess\n", + " my_prep_circuit = qaoa_max_cut_circuit(\n", + " max_cut_graph_edges, n_nodes, guess_mixer_angles, guess_cost_angles\n", + " )\n", + " measured_circ = my_prep_circuit.copy().measure_all()\n", + " compiler_pass(measured_circ)\n", + " res = backend.run_circuit(measured_circ, shots, seed=seed)\n", + "\n", + " return get_max_cut_energy(max_cut_graph_edges, res)" + ] + }, + { + "cell_type": "markdown", + "id": "2c01c28b", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Optimise Energy by Guessing Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "0a44bed8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_optimise_energy(compiler_pass: Callable[[Circuit], bool],\n", + " backend: Backend,\n", + " iterations: int = 100,\n", + " n: int = 3,\n", + " shots: int = 5000,\n", + " seed: int= 12345):\n", + " \n", + " highest_energy = 0 \n", + " best_guess_mixer_angles = [0 for i in range(n)] \n", + " best_guess_cost_angles = [0 for i in range(n)]\n", + " rng = np.random.default_rng(seed)\n", + " # guess some angles (iterations)-times and try if they are better than the best angles found before\n", + " \n", + " for i in range(iterations):\n", + " \n", + " guess_mixer_angles = rng.uniform(0, 1, n)\n", + " guess_cost_angles = rng.uniform(0, 1, n)\n", + " \n", + " qaoa_energy = qaoa_instance(backend,\n", + " compiler_pass,\n", + " guess_mixer_angles,\n", + " guess_cost_angles,\n", + " seed=seed,\n", + " shots=shots)\n", + " \n", + " if(qaoa_energy > highest_energy):\n", + " \n", + " print(\"new highest energy found: \", qaoa_energy)\n", + " \n", + " best_guess_mixer_angles = np.round(guess_mixer_angles, 3)\n", + " best_guess_cost_angles = np.round(guess_cost_angles, 3)\n", + " highest_energy = qaoa_energy\n", + " \n", + " print(\"highest energy: \", highest_energy)\n", + " print(\"best guess mixer angles: \", best_guess_mixer_angles)\n", + " print(\"best guess cost angles: \", best_guess_cost_angles)\n", + " return best_guess_mixer_angles, best_guess_cost_angles" + ] + }, + { + "cell_type": "markdown", + "id": "d22226cc", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Calculate the State for the final Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "da46e63d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_calculate(backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " shots: int = 5000,\n", + " iterations: int = 100,\n", + " seed: int = 12345,\n", + " ) -> BackendResult:\n", + " \n", + " # find the parameters for the highest energy\n", + " best_mixer, best_cost = qaoa_optimise_energy(compiler_pass,\n", + " backend,\n", + " iterations,\n", + " 3,\n", + " shots=shots,\n", + " seed=seed)\n", + " \n", + " # get the circuit with the final parameters of the optimisation:\n", + " my_qaoa_circuit = qaoa_max_cut_circuit(max_cut_graph_edges,\n", + " n_nodes,\n", + " best_mixer,\n", + " best_cost)\n", + "\n", + " my_qaoa_circuit.measure_all()\n", + "\n", + " compiler_pass(my_qaoa_circuit)\n", + " handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed)\n", + "\n", + " result = backend.get_result(handle) \n", + " \n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "9dd97e10", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Results with the Noiseless Simulator" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "e7afb38e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "aaea7e2f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new highest energy found: 3.1432\n", + "new highest energy found: 3.283599999999999\n", + "new highest energy found: 4.361\n", + "new highest energy found: 4.925600000000001\n", + "new highest energy found: 4.941999999999999\n", + "highest energy: 4.941999999999999\n", + "best guess mixer angles: [0.392 0.247 0.138]\n", + "best guess cost angles: [0.592 0.738 0.608]\n", + "CPU times: user 35.1 s, sys: 7.19 ms, total: 35.1 s\n", + "Wall time: 35.1 s\n" + ] + } + ], + "source": [ + "%%time\n", + "res = qaoa_calculate(backend, backend.default_compilation_pass(2).apply, shots = 5000, iterations = 100, seed=12345)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "3b86301e-7645-4553-be38-3ebf89eedd37", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success ratio 0.4252 \n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from maxcut_plotting import plot_maxcut_results\n", + "\n", + "plot_maxcut_results(res, 6)" + ] + }, + { + "cell_type": "markdown", + "id": "6e36c4fb-a118-4ab8-be01-77a674f273e3", + "metadata": {}, + "source": [ + "Here the binary strings in the results correspond to the two optimal colourings of our graph." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ffce2a97-902c-44d8-8d64-b25498752907", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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uZLHHnuMlJQUrrvuOtNxXELF50L/+Mc/+P7771myZInpKCIil7V7925uv/12kpKSiPO0naWdSMXnQrm5ucTExDBr1izi4+NNxxERKVFOTg6tWrViwIABDBo0yHQcl1LxudiSJUsYN24cW7dupfyVbHYnIuJiDoeDnj17EhISwsyZM33yvN75tLjFxbp27UrlypWZMWOG6SgiIsV69dVX2b17N2+//bbPlx5oxOcW27dv59577yUtLY3KlSubjiMi8ocvv/ySPn36sGXLFmrVqmU6jluo+NzkySefJCAggKlTp5qOIiICwN69e4mNjWXRokW2Woeg4nOTo0ePEhMTQ3JyMjfccIPpOCJic6dOnaJ169b07duXoUOHmo7jVio+N5o6dSrLly9n1apVtphHFxHP5HA46PP77RPmzp1ru59HWtziRk8++SS//PILn3zyiekoImJjU6dOZceOHT65HVlpaMTnZqtXr2bgwIHs2LGD4OBg03FExGaSk5Pp0aMHmzZtom7duqbjGKERn5u1bduWJk2aMGXKFNNRRMRmMjIy6NmzJ/PmzbNt6YFGfEbs3r2bli1b8u9//5saNWqYjiMiNpCbm0tcXBw9evQgISHBdByjVHyGjBo1igMHDjBnzhzTUUTExzkcDh599FHy8vJYuHChLc/rnU/FZ8jJkyeJjo5m6dKlxMbGmo4jIj7szTff5N1332Xjxo26RygqPqPmzJnDtGnT2LRpE/7+Ot0qIs63fv16HnroIb7++msaNGhgOo5H0E9bgx555BH8/f013SkiLnHgwAEefvhhZs+erdI7j0Z8hm3ZsoVOnTqRlpZGxYoVTccRER9x+vRp7rzzTh588EFGjRplOo5HUfF5gH79+hEVFcWkSZNMRxERH/HEE0/w22+/sWTJEtsvZrmQis8DHDx4kCZNmrBx40YaNmxoOo6IeLnp06fz+uuvs2nTJipUqGA6jsdR8XmISZMmsWHDBm1nJiJXZePGjTz44INs2LBBX6RLoMUtHmLo0KGkpqbyxRdfmI4iIl7q4MGDdOvWjQ8++ECldwkqPg8RFBTE5MmTGTZsGGfOnDEdR0S8TH5+Pl27dmXAgAF06NDBdByPpuLzIB07dqRWrVq8+eabpqOIiJcZOnQoVatW5fnnnzcdxePpHJ+HSU1NpU2bNuzcuZOqVauajiMiXmDmzJm8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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "G = nx.Graph()\n", + "G.add_edges_from(max_cut_graph_edges)\n", + "\n", + "H = nx.Graph()\n", + "H.add_edges_from(max_cut_graph_edges)\n", + "\n", + "plt.figure(1)\n", + "nx.draw(G, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['red', 'blue', 'red','red', 'blue', 'red', 'red'])\n", + "plt.figure(2)\n", + "nx.draw(H, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['blue', 'red', 'blue', 'blue', 'red', 'blue', 'blue'])\n", + "\n", + "plt.show()" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "vscode": { + "interpreter": { + "hash": "3289aa74b4cc5b65254d7b081e6c83acb4efa1b1c1d2fe845644451ee4b44b02" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/team_solutions/pineapple/maxcut_notebook_precompute_all.ipynb b/team_solutions/pineapple/maxcut_notebook_precompute_all.ipynb new file mode 100644 index 0000000..bb3585f --- /dev/null +++ b/team_solutions/pineapple/maxcut_notebook_precompute_all.ipynb @@ -0,0 +1,1377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3ba3449", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The Quantum Approximate Optimisation Algorithm (QAOA) using TKET, optimizing Compilation Time" + ] + }, + { + "cell_type": "markdown", + "id": "45668632", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## The Max-Cut problem" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9456fbeb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "G = nx.Graph()\n", + "G.add_edges_from([(0,1), (1,2), (2,0)])\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, node_color=['red', 'blue', 'red'])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cad41481", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "There are \\\\(2^3\\\\) possible assignments of colour to nodes. In general there are \\\\( 2^n \\\\). The Max-cut problem can then be stated as that of finding the colour assignment which maximises the number of edges between vertices of a different colour." + ] + }, + { + "cell_type": "markdown", + "id": "7389bbe5", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Quantum Approximate Optimization Algorithm (QAOA)\n", + "\n", + "Introduced in 'A Quantum Approximate Optimization Algorithm' (found at https://arxiv.org/abs/1411.4028). The idea is to prepare a quantum state which encodes a solution to the Max-cut problem.\n", + "\n", + "\n", + "This is a variational algorithm, which is to say that a paramaterised state is prepared, with the parameters varied to improve the solution. We will have $2p$ parameters where p is our number of layers. In particular, the state prepared has the form \n", + "\n", + "\n", + "\n", + "\\\\[ \\left| \\psi \\left( \\beta, \\gamma \\right) \\right\\rangle = U \\left( \\beta_m \\right) U \\left( \\gamma_m \\right) ... U \\left( \\beta_0 \\right) U \\left( \\gamma_0 \\right) \\left| \\psi_0 \\right\\rangle \\\\]\n", + "where\n", + "\\\\[ U \\left( \\beta_i \\right) = e^{i \\beta H_B} \\quad \\& \\quad U \\left( \\gamma_i \\right) = e^{i \\gamma H_P} \\\\]\n", + "with \\\\( H_B \\\\) and \\\\( H_P \\\\) depending on the problem instance. " + ] + }, + { + "cell_type": "markdown", + "id": "3596e66d", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "d720b387", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "For the previous 3 vertex graph the *problem Hamiltonian* is\n", + "\\\\[ H_P = \\frac{1}{2} \\big[ \\left( Z \\otimes Z \\otimes I \\right) + \\left( Z \\otimes I \\otimes Z \\right) + \\left( I \\otimes Z \\otimes Z \\right) \\big] \\\\]\n", + "\n", + "\n", + "where you will notice that there is a \\\\( Z \\otimes Z \\\\) acting between each vertex which is connected by an edge." + ] + }, + { + "cell_type": "markdown", + "id": "8ca2e1b9", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "The *mixer Hamiltonian* has the form \n", + "\\\\[ H_B = \\left( X \\otimes I \\otimes I \\right) + \\left( I \\otimes X \\otimes I \\right) + \\left( I \\otimes I \\otimes X \\right) \\\\]\n", + "\n", + "\n", + "where you will notice that there is an \\\\( X \\\\) acting on each vertex." + ] + }, + { + "cell_type": "markdown", + "id": "de6b9e03", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "\n", + "A solution to maxcut can be found by maximising the following cost function $C$ .\n", + "\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here $x_i$ and $x_j$ are the the \"colours\" of each vertex. \n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i,x_j \\in \\{0,1\\}\n", + "\\end{equation}\n", + "$$\n", + "\n", + "$x_i(1-x_j)=0$ if $x_i=x_j$ and $ x_i(1-x_j)=1$ if the terms are not equal." + ] + }, + { + "cell_type": "markdown", + "id": "61d4e798", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We want to encode our Maxcut cost function as a Hamiltonain. To do this we can perform the following translation.\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i \\mapsto \\frac{1}{2}(1-Z_i)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "The Pauli Z operator can be used to distinguish between the $|0\\rangle$ and $|1\\rangle$ basis states as these are eigenstates with eigenvalues $\\pm 1$ .\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_B = \\sum_i X_i\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here we use the the convention that $X_i$ means a Pauli X operator will be applied to the \"ith\" qubit and the identity operator will be applied to all other qubits in the circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "aeb6abd9", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "id": "3cbb783a", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Circuit Construction for QAOA" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "688f1332", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "image/png": "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e7cmTpwY65EAWAQrPljO7t27VVBQoJycHL355ptED0BYWPHBMrxer371q1/pj3/8o/70pz9p3rx5sR4JgAURPljCe++9p4ULFyolJUVVVVXKysqK9UgALIpTnTC9J554QtOnT9eCBQv04osvEj0AZ4UVH0yrra1N3//+97V371699NJLmjJlSqxHAjACsOKDKe3bt0+FhYVyuVyqqKggegCGDCs+mEogEND999+vBx54QKtXr9aCBQtiPRKAEYbwwTSOHz+uW265Rd3d3dq/f78+97nPxXokACMQpzphCs8++6yKiopUXFysnTt3Ej0AUcOKDzHV2dmpu+66Sy+88IKeeeYZzZo1K9YjARjhWPEhZt566y1NmzZNzc3Nqq6uJnoAhgXhw7AzDEMPPfSQrrnmGt1111168sknlZqaGuuxAMQJTnViWDU1Nen222/XBx98oD179ugLX/hCrEcCEGdY8WHYbN++XQUFBcrPzyd6AGKGFR+irru7W/fcc482bdqk9evX69prr431SADiGOFDWJrau1RWUaea+la1enxKcTuVn5WiBUU5ShudEHT8O++8o5tuuknZ2dmqrq5Wenp6DKYGgE/ZDMMwYj0EzO/AsRatLq/VriONkqQuX+D0fW6nXYakkrx0LSnO1eTxqTIMQ+vXr9dPfvIT/eIXv9CSJUtks9liND0AfIrwYUAb9x7Vim018vj86u9Pi80muZ0OLZt9gXY++iu99dZbevLJJ3XZZZcN37AAMADCh36dit4hdXoDAx/cw9elS73vqOy+HyoxMTF6wwFABLiqE306cKxFK7bVhIye9+RxvX//DWraujL4gc4EvZsyWe80dw3DlAAQHsKHPq0ur5XH5w9538mX1yohu+/tCB6fX2vKa6M1GgBEjPAhpKb2Lu060hjyNb2Og7tkd58j9wWT+3y8YUg7DzequZ1VHwBzIXwIqayiLuTtga5/qOW1TRp7zXcHfA6bpLLK0M8DALFC+BAkEAio+mhjry0LPVp2b9DoyXPkTDl3wOfx+AKqOdEWjREBIGKW2cAe7sZp9BYIBNTU1KSGhobTX/X19SF/3tTUpMwF/ybnBYW9nqO74V153j+g7Nt+P+j/bqvHO9S/FAA4K6bfzhDuxul4MlDMzvxxU1OTxowZo8zMzNNfWVlZIX+ckZGhn2x5W/9Z/UGv/17rvmfVsvtx2Uad2qJgdHskIyBX2vg+Y3hDwfladWNBtH8rAGDQTL3iG2jjtOeTCL58sEG7jzRp+bx8lc6cMLxDDrHPxqyvVVmomJ0Zr0suuSQoZi6Xa9Bz5GelKMFZ3+sfGqML5uqcS64+/fPWv26R7+MGjZu7NORzuJ125WcnR/6bAQBRYNrw9bVxuuPgLrW88aT8rY1ynDNWaV/5n3KP/6I6vX6t2HZIkkwXP7/fr+bm5pjHLBzzi3K0avuRXrfZXW7J5T79c5vLLZtzlBxJY0I+hyFp/pScqMwHAJEyZfj62jjd+V6VPir/v0r/2s806ryL5W8/2ft+b0ArttVoUk6qJuWkRnXG/mL22bD1F7OJEyf2Ov0YzZiF49zRCSq+OF2vHGro823KUq9a1OfjbTZpdl46r78CMB1Thq+vjdMfv75JY2bdpITz8yVJzuTgKwt7Nk6vLZ0a9n/3zJj1tyr7bMzODJmZYxaupSW5eu2dJnV6Q29i74/b6dCSktwoTAUAZ8d04etr47QR8KvrRK0Sc2fo+NrvyfB3K+kLM5U6+3bZXZ+uKs7cOJ02OmHQMauvr1dzc/OgYpaVlaX09HRLxiwck8enavm8/LDfqzPRZdfyeflRX3UDQCRMF76+Nk77O1qkgE//OPyGMkt/J5vdocZn/l0f79msscW39Dq2u7tLs0r/l1r2lvUZs6ysrLiMWbh6Xi8N59MZRsJFRgBGLtOFr6a+NeTGadsnq7rkouvlHD3u1I+nfT1k+Ay7SzPm3qD71vxvYjYESmdO0KScVK0pr9XOw42y6dMraqVPt5XMzkvXkpJcVnoATM104Wv1+ELe7nCPluMzr+n198GmzqQUnXfeeUM6WzyblJOqtaVT1dzepbLKOt2z8mF99YYFSk1KUH52suZP4Y0EAFiD6cKX4u57pNGXXau2iueVeGGR5HCqdd9/Kil3Wh/PwyovGtJGJ+iOqy/S0mv+Q48+cx+raQCWY7rwhdo43WPMrG/J39mq44/eIZvTpXPyr9KYK24MOo6N09FnGEa/K24AMCvTvWVZU3uXZv3u1ZDhG6wEp117fnYNp96iyG63y+v1yuFwxHoUAAiL6T6doWfjdKSLCTZODw9WfACsynThk05tnHY7I1tJsHF6+BA+AFZkyvD1bJxOdIU3Hhunh0fP2XHCB8CKTHdxSw82TpuXyV4WBoCwmO7ils/6W10LG6dNxu/3y+VyKRCI/AIkAIgV04evR8/G6ZoTbWr1eJXidrFxOkZ8Pp8SEk69DyoAWI1lwgfz8Hq9SkxMlM8X+l12AMDMTHlxC8yNrQwArIzwIWyED4CVET6EjfABsDLCh7ARPgBWRvgQNsIHwMoIH8JG+ABYGeFD2AgfACsjfAgb4QNgZYQPYSN8AKyM8CFshA+AlRE+hI3wAbAywoewET4AVkb4EDbCB8DKCB/CRvgAWBnhQ9gIHwArI3wIG+EDYGWED2EjfACsjPAhIoQPgFURPoTNMIxYjwAAESN8CBunOgFYGeFD2AgfACsjfAgb4QNgZYQPYSN8AKyM8CFshA+AlRE+hI3wAbAywoewET4AVkb4EDbCB8DKCB/CRvgAWBnhQ9gIHwArI3wIG+EDYGWED2EjfACsjPAhbIQPgJXZDN5qH4PU1N6lsoo67TtyXK++/hd9bd4c5WelaEFRjtJGJ8R6PAAYFMKHAR041qLV5bXadaRRktTlC5y+z+20y5BUkpeuJcW5mjw+NTZDAsAgET70a+Peo1qxrUYen1/9/Umx2SS306Hl8/JVOnPCsM0HAOFyxnoAmNep6B1Spzcw4LGGIXV6/Vqx7ZAkET8ApsWKDyEdONaibz22V51ef6/b6zf9XF0fHJbN7pAkOZLTdP7iR3odk+hyaPPimZqUkzpc4wLAoLHiQ0iry2vl8flD3jduzr8oefLcPh/r8fm1prxWa0unRms8AIgY2xkQpKm9S7uONPb7ml5/DEPaebhRze1dQzsYAAwBwocgZRV1/d7fUr5ex36/UPUbfiLP+38LeYxNUlll/88DALHAqU4Eqalv7bVl4UxjZ98mV9p42RwudRzarQ+f+bWyb/uDXGOzex3n8QVUc6JtOMYFgLCw4kOQVo+vz/sSzsuTPSFJNqdLoy/7khLOv0Sd/72/j+fxRmtEAIgY4UOQFHcYJwJsNkmhXwxMcbuGZiAAGEKED0Hys1KU4Az+oxHwtKvz3QoZvm4ZAb/a/2unuo69rcQLi4KOdTvtys9OHo5xASAsvMaHIPOLcrRq+5Gg242AXy27N8p7sk6y2eVKy1H6N+6Ra9z5wcdKmj8lZximBYDwED4EOXd0goovTtcrhxp6bWlwJI1R9q2rBny8zSbNzkvnjasBmBKnOhHS0pJcuZ2OiB7rdjq0pCR3iCcCgKFB+BDS5PGpWj4vX4mu8P6IJLrsWj4vn7crA2BanOpEn3reaJpPZwAwkvAm1RjQ3+patKa8VjsPN8qmU5vTe/R8Ht/svHQtKcllpQfA9AgfBq25vUtllXWqOdGmVo9XKW6X8rOTNX8Kn8AOwDoIHwAgrnBxCwAgrhA+AEBcIXwAgLhC+AAAcYXwAQDiCuEDAMQVwgcAiCuEDwAQVwgfACCuED4AQFwhfACAuEL4AABxhfABAOIK4QMAxBXCBwCIK4QPABBXCB8AIK4QPgBAXCF8AIC4QvgAAHGF8AEA4sr/Bw3unwoQB3gdAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "\n", + "max_cut_graph_edges = [(0,1), (1,2), (1,3), (3,4), (4,5), (4,6)]\n", + "n_nodes = 7\n", + "\n", + "max_cut_graph = nx.Graph()\n", + "max_cut_graph.add_edges_from(max_cut_graph_edges)\n", + "nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()})\n", + "\n", + "expected_results = [(0,1,0,0,1,0,0), (1,0,1,1,0,1,1)]" + ] + }, + { + "cell_type": "markdown", + "id": "18a5bd16", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define Cost Hamiltonian: $\\gamma H$" + ] + }, + { + "cell_type": "markdown", + "id": "543f87ca", + "metadata": {}, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "99226b24", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(): 3.00000000000000, (Zq[0], Zq[1]): -0.500000000000000, (Zq[1], Zq[2]): -0.500000000000000, (Zq[1], Zq[3]): -0.500000000000000, (Zq[3], Zq[4]): -0.500000000000000, (Zq[4], Zq[5]): -0.500000000000000, (Zq[4], Zq[6]): -0.500000000000000}\n" + ] + } + ], + "source": [ + "from typing import List, Tuple, Any\n", + "from pytket.utils import QubitPauliOperator\n", + "from pytket.pauli import QubitPauliString, Pauli\n", + "from pytket import Qubit\n", + "\n", + "def qaoa_graph_to_cost_hamiltonian(edges: List[Tuple[int, int]], cost_angle: float) -> QubitPauliOperator:\n", + " qpo_dict = {QubitPauliString(): len(edges)*0.5*cost_angle}\n", + " for e in edges:\n", + " term_string = QubitPauliString([Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z])\n", + " qpo_dict[term_string] = -0.5*cost_angle\n", + " return QubitPauliOperator(qpo_dict)\n", + "\n", + "cost_angle = 1.0\n", + "cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost_angle)\n", + "print(cost_ham_qpo)" + ] + }, + { + "cell_type": "markdown", + "id": "6da499ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = 3 I^{\\otimes 6} -0.5 \\big[ Z_0 Z_1 + Z_1 Z_2 +Z_1 Z_3 +Z_3 Z_4 +Z_4 Z_5 +Z_4 Z_6 \\big]\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Using the same index convention as above" + ] + }, + { + "cell_type": "markdown", + "id": "785ff56c", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Hamiltonian Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "11fe9917", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.utils import gen_term_sequence_circuit\n", + "from pytket import Circuit\n", + "from pytket.circuit import display\n", + "\n", + "cost_ham_circuit = gen_term_sequence_circuit(cost_ham_qpo, Circuit(n_nodes))\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9057c55f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.transform import Transform\n", + "\n", + "Transform.DecomposeBoxes().apply(cost_ham_circuit)\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4690b787", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construction of the Mixer Hamiltonian: $\\beta B$" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "296c560d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mixer_angle = 0.8\n", + "mixer_ham_qpo = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer_angle for i in range(n_nodes)})\n", + "mixer_ham_circuit = gen_term_sequence_circuit(mixer_ham_qpo, Circuit(n_nodes))\n", + "Transform.DecomposeBoxes().apply(mixer_ham_circuit)\n", + "display.render_circuit_jupyter(mixer_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4d128a70", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define the Initial State" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0a9db628", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pytket.backends.backend import Backend\n", + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "def qaoa_initial_circuit(n_qubits: int) -> Circuit:\n", + " c = Circuit(n_qubits)\n", + " for i in range(n_qubits):\n", + " c.H(i)\n", + " return c\n", + "\n", + "superposition_circuit = qaoa_initial_circuit(n_nodes)\n", + "\n", + "display.render_circuit_jupyter(superposition_circuit)\n", + "\n", + "Transform.DecomposeBoxes().apply(superposition_circuit)\n", + "backend.default_compilation_pass(2).apply(superposition_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "da759b59", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construct QAOA Circuit" + ] + }, + { + "cell_type": "markdown", + "id": "359a1a0f-e92e-40ae-bbe6-ce960b118f49", + "metadata": {}, + "source": [ + "Now lets define a function to create our entire QAOA circuit. For $p$ QAOA layers we expect that our circuit will require $2p$ parameters. Here we will pass and cost mixer parameters in as a list where the length of the list defines the number of layers." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "5e97191f-9d15-4f8a-bc2a-70ea60e78553", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "32\n", + "64\n", + "96\n", + "128\n", + "160\n", + "192\n", + "224\n", + "256\n", + "288\n", + "320\n", + "352\n", + "384\n", + "416\n", + "448\n", + "480\n", + "512\n", + "544\n", + "576\n", + "608\n", + "640\n", + "672\n", + "704\n", + "736\n", + "768\n", + "800\n", + "832\n", + "864\n", + "896\n", + "928\n", + "960\n", + "992\n", + "1024\n" + ] + } + ], + "source": [ + "cost_circuits = []\n", + "mixer_circuits = []\n", + "\n", + "for i in range(1025):\n", + " cost_circuit = gen_term_sequence_circuit(qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, i/1024), Circuit(n_nodes))\n", + " mixer_circuit = gen_term_sequence_circuit(QubitPauliOperator({QubitPauliString([Qubit(j)], [Pauli.X]): i/1024 for j in range(n_nodes)}), Circuit(n_nodes))\n", + " Transform.DecomposeBoxes().apply(cost_circuit)\n", + " Transform.DecomposeBoxes().apply(mixer_circuit)\n", + " backend.default_compilation_pass(2).apply(cost_circuit)\n", + " backend.default_compilation_pass(2).apply(mixer_circuit)\n", + " cost_circuits.append(cost_circuit)\n", + " mixer_circuits.append(mixer_circuit)\n", + " if (i % 32 == 0):\n", + " print(i)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "23f8910a", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_max_cut_circuit(edges: List[Tuple[int, int]],\n", + " n_nodes: int,\n", + " mixer_angles: List[int],\n", + " cost_angles: List[int]) -> Circuit:\n", + " \n", + " assert len(mixer_angles) == len(cost_angles)\n", + " \n", + " # initial state\n", + " qaoa_circuit = superposition_circuit.copy()\n", + " \n", + " \n", + " \n", + " # add cost and mixer terms to state\n", + " for cost, mixer in zip(cost_angles, mixer_angles):\n", + " qaoa_circuit.append(cost_circuits[cost])\n", + " qaoa_circuit.append(mixer_circuits[mixer])\n", + " \n", + " print(qaoa_circuit)\n", + " \n", + " return qaoa_circuit" + ] + }, + { + "cell_type": "markdown", + "id": "bc2f8939-41b7-476b-a5a8-09de07211079", + "metadata": {}, + "source": [ + "We also need to extract our energy expectation values from a `BackendResult` object after our circuit is processed by the device/simulator. We do this with the `get_max_cut_energy` function below. Note that the fact that the maxcut Hamiltonian contains only commuting terms means that we do not need to calculate our energy expectation using multiple measurement circuits. This may not the the case for a different problem Hamiltonian." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "df387eea-4198-428e-9b92-4f3bceb12f0e", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple\n", + "from pytket.backends.backendresult import BackendResult\n", + "\n", + "def get_max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult) -> float:\n", + " energy = 0.0\n", + " dist = results.get_distribution()\n", + " for i, j in edges:\n", + " energy += sum((meas[i] ^ meas[j]) * prob for meas, prob in dist.items())\n", + "\n", + " return energy" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e5abad7b-e989-4156-9708-3d8c97d8ca2a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "from typing import Callable\n", + "import numpy as np\n", + "\n", + "def qaoa_instance(\n", + " backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " guess_mixer_angles: np.array,\n", + " guess_cost_angles: np.array,\n", + " seed: int,\n", + " shots: int = 5000,\n", + ") -> float:\n", + " \n", + " # print(\"test1\")\n", + " # step 1: get state guess\n", + " my_prep_circuit = qaoa_max_cut_circuit(\n", + " max_cut_graph_edges, n_nodes, guess_mixer_angles, guess_cost_angles\n", + " )\n", + " # print(\"test2\")\n", + " measured_circ = my_prep_circuit.copy().measure_all()\n", + " # print(\"test31\")\n", + " res = backend.run_circuit(measured_circ, shots, seed=seed)\n", + " # print(\"test3\")\n", + "\n", + " return get_max_cut_energy(max_cut_graph_edges, res)" + ] + }, + { + "cell_type": "markdown", + "id": "2c01c28b", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Optimise Energy by Guessing Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "0a44bed8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_optimise_energy(compiler_pass: Callable[[Circuit], bool],\n", + " backend: Backend,\n", + " iterations: int = 100,\n", + " n: int = 3,\n", + " shots: int = 5000,\n", + " seed: int= 12345):\n", + " \n", + " highest_energy = 0 \n", + " best_guess_mixer_angles = [0 for i in range(n)] \n", + " best_guess_cost_angles = [0 for i in range(n)]\n", + " rng = np.random.default_rng(seed)\n", + " # guess some angles (iterations)-times and try if they are better than the best angles found before\n", + " \n", + " for i in range(iterations):\n", + " \n", + " guess_mixer_angles = rng.integers(0, 1025, n)\n", + " guess_cost_angles = rng.integers(0, 1025, n)\n", + " \n", + " qaoa_energy = qaoa_instance(backend,\n", + " compiler_pass,\n", + " guess_mixer_angles,\n", + " guess_cost_angles,\n", + " seed=seed,\n", + " shots=shots)\n", + " \n", + " if(qaoa_energy > highest_energy):\n", + " \n", + " print(\"new highest energy found: \", qaoa_energy)\n", + " \n", + " best_guess_mixer_angles = np.round(guess_mixer_angles, 3)\n", + " best_guess_cost_angles = np.round(guess_cost_angles, 3)\n", + " highest_energy = qaoa_energy\n", + " print(\"iteration: \", i)\n", + " \n", + " print(\"highest energy: \", highest_energy)\n", + " print(\"best guess mixer angles: \", best_guess_mixer_angles)\n", + " print(\"best guess cost angles: \", best_guess_cost_angles)\n", + " return best_guess_mixer_angles, best_guess_cost_angles" + ] + }, + { + "cell_type": "markdown", + "id": "d22226cc", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Calculate the State for the final Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "da46e63d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_calculate(backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " shots: int = 5000,\n", + " iterations: int = 100,\n", + " seed: int = 12345,\n", + " ) -> BackendResult:\n", + " \n", + " # find the parameters for the highest energy\n", + " best_mixer, best_cost = qaoa_optimise_energy(compiler_pass,\n", + " backend,\n", + " iterations,\n", + " 3,\n", + " shots=shots,\n", + " seed=seed)\n", + " \n", + " # get the circuit with the final parameters of the optimisation:\n", + " my_qaoa_circuit = qaoa_max_cut_circuit(max_cut_graph_edges,\n", + " n_nodes,\n", + " best_mixer,\n", + " best_cost)\n", + "\n", + " my_qaoa_circuit.measure_all()\n", + "\n", + " compiler_pass(my_qaoa_circuit)\n", + " handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed)\n", + "\n", + " result = backend.get_result(handle) \n", + " \n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "9dd97e10", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Results with the Noiseless Simulator" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "e7afb38e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "aaea7e2f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "new highest energy found: 1.8970000000000002\n", + "iteration: 0\n", + "\n", + "new highest energy found: 2.1770000000000005\n", + "iteration: 1\n", + "\n", + "new highest energy found: 3.0746\n", + "iteration: 2\n", + "\n", + "iteration: 3\n", + "\n", + "iteration: 4\n", + "\n", + "new highest energy found: 3.3115999999999994\n", + "iteration: 5\n", + "\n", + "iteration: 6\n", + "\n", + "iteration: 7\n", + "\n", + "new highest energy found: 4.275399999999999\n", + "iteration: 8\n", + "\n", + "iteration: 9\n", + "\n", + "iteration: 10\n", + "\n", + "iteration: 11\n", + "\n", + "iteration: 12\n", + "\n", + "iteration: 13\n", + "\n", + "iteration: 14\n", + "\n", + "iteration: 15\n", + "\n", + "iteration: 16\n", + "\n", + "iteration: 17\n", + "\n", + "iteration: 18\n", + "\n", + "iteration: 19\n", + "\n", + "iteration: 20\n", + "\n", + "iteration: 21\n", + "\n", + "iteration: 22\n", + "\n", + "iteration: 23\n", + "\n", + "iteration: 24\n", + "\n", + "iteration: 25\n", + "\n", + "iteration: 26\n", + "\n", + "iteration: 27\n", + "\n", + "iteration: 28\n", + "\n", + "iteration: 29\n", + "\n", + "iteration: 30\n", + "\n", + "iteration: 31\n", + "\n", + "iteration: 32\n", + "\n", + "iteration: 33\n", + "\n", + "iteration: 34\n", + "\n", + "iteration: 35\n", + "\n", + "iteration: 36\n", + "\n", + "iteration: 37\n", + "\n", + "iteration: 38\n", + "\n", + "iteration: 39\n", + "\n", + "iteration: 40\n", + "\n", + "new highest energy found: 4.7468\n", + "iteration: 41\n", + "\n", + "iteration: 42\n", + "\n", + "iteration: 43\n", + "\n", + "iteration: 44\n", + "\n", + "iteration: 45\n", + "\n", + "iteration: 46\n", + "\n", + "iteration: 47\n", + "\n", + "iteration: 48\n", + "\n", + "iteration: 49\n", + "\n", + "iteration: 50\n", + "\n", + "iteration: 51\n", + "\n", + "iteration: 52\n", + "\n", + "iteration: 53\n", + "\n", + "iteration: 54\n", + "\n", + "iteration: 55\n", + "\n", + "iteration: 56\n", + "\n", + "new highest energy found: 4.8962\n", + "iteration: 57\n", + "\n", + "iteration: 58\n", + "\n", + "iteration: 59\n", + "\n", + "iteration: 60\n", + "\n", + "iteration: 61\n", + "\n", + "iteration: 62\n", + "\n", + "iteration: 63\n", + "\n", + "iteration: 64\n", + "\n", + "iteration: 65\n", + "\n", + "iteration: 66\n", + "\n", + "iteration: 67\n", + "\n", + "iteration: 68\n", + "\n", + "iteration: 69\n", + "\n", + "iteration: 70\n", + "\n", + "iteration: 71\n", + "\n", + "iteration: 72\n", + "\n", + "iteration: 73\n", + "\n", + "iteration: 74\n", + "\n", + "iteration: 75\n", + "\n", + "iteration: 76\n", + "\n", + "iteration: 77\n", + "\n", + "iteration: 78\n", + "\n", + "iteration: 79\n", + "\n", + "iteration: 80\n", + "\n", + "iteration: 81\n", + "\n", + "iteration: 82\n", + "\n", + "iteration: 83\n", + "\n", + "iteration: 84\n", + "\n", + "iteration: 85\n", + "\n", + "iteration: 86\n", + "\n", + "iteration: 87\n", + "\n", + "iteration: 88\n", + "\n", + "iteration: 89\n", + "\n", + "iteration: 90\n", + "\n", + "iteration: 91\n", + "\n", + "iteration: 92\n", + "\n", + "iteration: 93\n", + "\n", + "iteration: 94\n", + "\n", + "iteration: 95\n", + "\n", + "iteration: 96\n", + "\n", + "iteration: 97\n", + "\n", + "iteration: 98\n", + "\n", + "iteration: 99\n", + "highest energy: 4.8962\n", + "best guess mixer angles: [293 132 142]\n", + "best guess cost angles: [401 814 329]\n", + "\n", + "CPU times: user 4.4 s, sys: 20.2 ms, total: 4.42 s\n", + "Wall time: 4.41 s\n" + ] + } + ], + "source": [ + "%%time\n", + "res = qaoa_calculate(backend, backend.default_compilation_pass(2).apply, shots = 5000, iterations = 100, seed=14839)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "3b86301e-7645-4553-be38-3ebf89eedd37", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success ratio 0.3734 \n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from maxcut_plotting import plot_maxcut_results\n", + "\n", + "plot_maxcut_results(res, 6)" + ] + }, + { + "cell_type": "markdown", + "id": "6e36c4fb-a118-4ab8-be01-77a674f273e3", + "metadata": {}, + "source": [ + "Here the binary strings in the results correspond to the two optimal colourings of our graph." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "ffce2a97-902c-44d8-8d64-b25498752907", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAb4AAAEuCAYAAADx63eqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAAsTAAALEwEAmpwYAAAfkklEQVR4nO3deXxV9Z3/8VcSsieETUAQRVHQAgmLIsvUImLr0lZqsasQFm0HOnXpqJ2fo60zYvuzap0BCk6hKExdqBSxDws6I6IFQTsiMUBV3BhBUbQSAiQhkGT+OIgN3CQ3cG/u9no+HnkQzvne6wd4eN/5fM/3nG9aQ0NDA5IkpYj0WBcgSVJbMvgkSSnF4JMkpRSDT5KUUgw+SVJKMfgkSSnF4JMkpRSDT5KUUgw+SVJKMfgkSSnF4JMkpRSDT5KUUgw+SVJKMfgkSSnF4JMkpRSDT5KUUgw+SVJKMfgkSSnF4JMkpRSDT5KUUgw+SVJKaRfrAhR/qqth7Vr4+OPg9506wciRkJ8f27okKRIMPh321lswcyYsWADp6dDQEBxPS4ODB2HCBLjuOjjzzJiWKUnHJa2h4dOPN6Wqhgb48Y9h1iyoq4MDB0KPa9cOMjODAJwzBzIy2rZOSYoEgy/FNTRAaSn8/vdQVRXea/LyYOxYeOyxoDOUpETix1aKu+OO1oUeBGOffhpuuCF6dUlStNjxpbC9e6Fr12Axy9E+AaYC/wV0AX4OfKfRiJwc2LYNunSJdqWSFDl2fCnst79tbqryB0AW8CHwIDAN2NxoRFoazJsXzQolKfLs+FJUQwP06QPvvBPq7D6gI7AJ6Hvo2ASgJ/D/G4084QT44AOv9UlKHH5cpai9e4NpytC2ENzp0vdvjpVwZMf36fvs2BHx8iQpagy+FFVRAVlZTZ3dC7Q/4lgRsOeokZmZsGtXREuTpKgy+FJUVhbU1zd1tgCoPOJYJVB41Mj6esjOjmxtkhRNBl+K6tQpuFk9tL7AQeCNvzn2CtD/qJG1tcF1PklKFAZfisrMhEsuCVZmHi0fuBz4CcFCl+eBxwkWuDQ2YgR06BC9OiUp0gy+FHbjjcFTWEKbA1QDXYFvA3M5suMrLISbbopmhZIUed7OkMIaGuCMM4KHUx+Lbt3gvfd8ZqekxGLHl8LS0mDp0mPbbigvD5YtM/QkJR6DL8UVF8Py5VBQ0NT1vqPl5cGSJTB8eHRrk6RoMPjEeefBiy/CqFHB8zczM48e064d5ObCsGGwejVcfHHb1ylJkeA1PjXy1lvBvnyPPgqVlbB37z66ds3h8sszuPZaN6GVlPgMPjXrjDPO4IknnqBfv36xLkWSIsKpTjWrY8eOVFRUxLoMSYoYg0/N6tChg8EnKakYfGqWwScp2Rh8alaHDh3Y5fYLkpKIwadmeY1PUrIx+NQspzolJRuDT81yqlNSsjH41CynOiUlG4NPzXKqU1KyMfjULKc6JSUbg0/NcqpTUrIx+NQspzolJRsfUq1m7d+/n4KCAmpra0kLd8M+SYpjdnxqVnZ2NpmZmVRVVcW6FEmKCINPLXK6U1IyMfjUIld2SkomBp9a5MpOScnE4FOLnOqUlEwMPrXIqU5JycTgU4vs+CQlE4NPLfIan6RkYvCpRXZ8kpKJwacWeY1PUjIx+NQipzolJRODTy1yqlNSMjH41CKnOiUlE4NPLXKqU1IyMfjUIqc6JSUT9+NTi+rq6sjKyuLAgQOkp/uzkqTE5qeYWpSRkUFBQQGVlZWxLkWSjpvBp7A43SkpWRh8CosrOyUlC4NPYXFlp6RkYfApLE51SkoWBp/C4lSnpGRh8CksTnVKShYGn8LiVKekZJEQwffyy3DlldCnD3TrBr16wYgR8NBDsH9/rKtLDU51SkoW7WJdQHOeeAJ+/GPYuhVqaqC+/rNz27fDpk3w938P06bBv/4rZGfHrNSkZ8cnKVnEbcd3553wjW/AX/4CVVWNQ+9Te/fCnj0waxaMGgV+LkeP1/gkJYu4DL5f/Sro4KqrwxtfXQ0bN8KFFzr1GS12fJKSRdwF35tvwo03Bl1eY/uBqcApQCEwCFhx+GxtbTD1OWNGGxWaYrzGJylZxF3wzZwJBw+GOnMQ6AU8B+wGZgDfALYeHlFTA7Nnw4ED0a8zpTQ00KW6mh47d0JZGWzbBm7qISlBxdW2RNXV0LVrcO0uPMXAT4GvHz5SWAgLFsD48VEoMNXs2QP/+Z9w1100fPABlTU1FLVvH/xk0b073HRTsNy2oCDWlUpS2OKq41uxAtLSwh39IbAF6N/o6J49wTVCHae5c4N7R266CbZuJa2mhiKAysrgJ5R33oEbbgjGzJ8f62olKWxxFXzvvRdcq2vZAeC7QClw5lFnt2+PbF0p55ZbglCrroZ9+5oet29fcDH22mvhX/6l7eqTpOMQV/fx1dRAXV1Lo+qBCUAWMDvkiO3bP+LKK6+nqKiIoqIiOnTocPj7UMfy8/NJC7/VTG7/8R9w772hVhc1raoKfvELOPlkmDw5erVJUgTE1TW+X/8arr++uc/cBmAKwYKW5UBuyFG9elUyY8Yydu/e3eiroqIi5LHa2tqjgjGcwPzb3+fk5CR+eFZVBRdZQ3R5s4EHgI3Atw99f5TCQti5E3JyolikJB2fuOr4hg1racQ04FXgaZoKvXbt4NJL2zNx4sSw/7sHDhxoMSDff/99Xn311SbH1dfXH1Ng/u1XdqwfPbN4cZMXWXsAtwBPAU3eXtnQAEuWBAteJClOxVXHBzBgAGzeHOrM/wK9gWwa5/V/EFzvC+TmwoYN0K9fFIsMoaam5qiwbK7LDHWsXbt2xxSYf3usXbvj+FnmzDPh9debHXILsJ0mOj4I/gE3bjz2GiQpyuKq44Pg2ZzTp4e6peEUgqnO5g0c2PahB5CTk0NOTg7dunU7ptc3NDRQXV3dYkC+8cYbTQZpZWUl2dnZYXWcRx7rWF9Pr3fe4bgna197LVj52b798b6TJEVF3HV8NTVB47F9ezgLXRrLzYXly2H06KiUFvcaGhrYt29fqzvN3bt30/Hjj/njjh20dEdeix1ffn7wCJ3evSP4J5OkyIm7ji8nB557DoYODR46HW745eUFixFTNfQA0tLSKCgooKCggJ49e7buxW+9BYMGtebpAaE1NEB6XN0lI0mNxOUn1CmnwPr1wer4lh4Kkp0ddHrz5sH3vtc29SWlzp0j84Tv2lro1On430eSoiQugw+C8HvtteChIIMHB+FWUBB0hHl5wcr5Dh2CB1q/+SZ85zuxrjjBdegAxcVNnj4I1AB1h75qDh07ytln+wgzSXEt7q7xNWXz5mC1ZkVFEII9e8IFF0BmZqwrSyKPPgpTpoSc7rwNOPLZLD89dPywwkJYtAjGjYtSgZJ0/BIm+NQGDhwIbmA/xn33qnJzyfrkE9p5A7ukOBa3U52KgcxMWLgwaKlbqT4nhzv69WPUF77Ali1bolCcJEWGwafGvvpV+Ld/a1345eaSPmcOt69fz8SJExk1ahS/+tWvcDJBUjxyqlOhPfFEcL2vpibY6ymUwsJgpdH998PFFx8+vGXLFiZMmEBRURELFizgpJNOaqOiJalldnwK7ctfhh074OGH4bzzgnvzsrMhKyv4fvTo4Nme77/fKPQA+vbty/PPP8/nP/95hgwZwkMPPWT3Jylu2PEpPHV1sHt38H1REWRkhPWy9evXM2HCBAYOHMicOXPo3LlzFIuUpJbZ8Sk8GRnBjemdOoUdegBDhw5l/fr19OzZk5KSElasWBHFIiWpZXZ8ajOrVq1i0qRJXHzxxdx9990UeKO7pBiw41ObOf/88ykvL6empoZBgwaxdu3aWJckKQXZ8SkmHnvsMaZPn87kyZO57bbbyMrKinVJklKEHZ9i4mtf+xplZWVs2rSJYcOGsdHNayW1EYNPMdOtWzcef/xxrrnmGsaMGcNdd91FXWs3YZSkVnKqU3HhnXfeYdKkSTQ0NLBw4UJOPfXUWJckKUnZ8SkunHrqqaxatYrLLruMYcOG8Zvf/Mab3iVFhR2f4s6mTZuYMGECvXr1Yt68eXTr1i3WJUlKInZ8ijsDBgzgxRdfZODAgZSUlLB06dJYlyQpidjxKa6tW7eOiRMnMnLkSGbOnElRUVGsS5KU4Oz4FNdGjBhBWVkZ+fn5FBcX88wzz8S6JEkJzo5PCePJJ5/kqquu4oorruBnP/sZucewYa4k2fEpYVx00UWUl5ezY8eOww+/lqTWsuNTQnrkkUe49tpr+cEPfsDNN99Mu3btYl2SpARh8Clhvffee0yZMoWKigoWLVpEv379Yl2SpATgVKcSVs+ePXnyyScpLS1l1KhRzJ49m/r6+liXJSnO2fEpKWzZsoWJEydSWFjI/fffz0knnRTrkiTFKTs+JYW+ffuyZs0aRo8ezZAhQ3jwwQd95JmkkOz4lHRefvllJkyYQP/+/Zk7dy6dO3eOdUmS4ogdn5LOkCFDWL9+Pb169aK4uJjly5fHuiRJccSOT0nt2WefZdKkSXzpS1/innvuoaCgINYlSYoxOz4ltdGjR1NeXk5tbS2DBg1i7dq1sS5JUozZ8SllLFu2jGnTpjFp0iRuu+02srOzY12SpBiw41PKGDduHK+88gqvvvoqw4YNY+PGjbEuSVIMGHxKKV27duWxxx7j+uuvZ8yYMdx1113U1dXFuixJbcipTqWsrVu3MmnSJOrq6li4cCGnnXZarEuS1Abs+JSyevfuzTPPPMPXvvY1zj33XObPn+9N71IKsOOTgM2bNzNhwgR69uzJvHnz6N69e6xLkhQldnwS0L9/f1544QVKSkoYNGgQS5cujXVJkqLEjk86wgsvvMDEiRMZPnw4M2fOpEOHDrEuSVIE2fFJRxg+fDgbNmygsLCQkpISVq5cGeuSJEWQHZ/UjKeeeoqpU6cyfvx4fv7zn5ObmxvrkiQdJzs+qRlf+tKXKC8vZ+fOnQwZMoSXXnop1iVJOk52fFKYFi9ezDXXXMP06dO5+eabyczMjHVJko6BwSe1wvvvv8+UKVP45JNPWLRoEWeeeWasS5LUSk51Sq3Qo0cPVqxYweTJk/n85z/PrFmzqK+vj3VZklrBjk86Rm+88QYTJ06koKCABQsW0KtXr7Bet38/VFRAVhYUFUG6P35Kbcr/5aRjdMYZZ7B69WrOP/98hg4dyoMPPtjkI8+qq+GBB+CssyAvD3r3hu7dIScHxo+HF14AfwSV2oYdnxQBGzZsYMKECZx11lnMnTuXLl26AEGY3Xsv/PSnwbi9e49+bXo65OZCjx6wZAkUF7dh4VIKsuOTImDw4MG89NJL9O7dm5KSEv74xz/S0ADTp8OttwaBFyr0AOrrYd8+eOMNGDkS/vSntq1dSjV2fFKEPffcc0yaNIlOnWby2mtfpqoqrVWvLygIpj77949SgVKKM/ikKNi8eQ8lJdnU1WWFOHslsBLYB3QHbgKuOnw2LQ2GD4e1a9ukVCnlONUpRcGiRYVkZIQKPYD/B2wFKoE/ALcA6w+fbWiADRuCqU9JkWfwSRFWWwv33Rf8Glp/IPvQ92mHvt5qNKKuDmbOjFqJUkoz+KQI+/Ofwxk1HcgDzgROBC5pdPbAAfjd7yJemiQMPiniPv44nFFzgD3AauByPusAP1NZGdGyJB1i8EkRFv4TzDKAvwO2A3OPOuuyMyk6DD4pwjp1au0rDnLkNT6AwsJIVCPpSAafFGHDhgWLU0LbCTwC7AXqgKeAh4ELGo3KyIBLL41ikVIKM/ikCMvLg9JSCL1dXxrBtOZJQEfgBuDfgK82GpWdDT/6UXTrlFKVN7BLUbBlCwwaFDyc+licfPIutm7tQFpa6576IqlldnxSFPTtG3R9eXmtf21OTh3p6dO54oor+OijjyJfnJTiDD4pSmbPhrFjWxd+ubnw0EMZvPrq/fTp04fi4mKWLl0avSKlFORUpxRF9fVwww0w99DdCjU1occVFgYb0/7+9/CFL3x2fO3atZSWlnLuuecya9YsOnbsGP2ipSRnxydFUXo6/PKXsHUr/PM/Q5cuQQfYvn0QdtnZcO65sHAhfPBB49ADGDlyJGVlZXTq1ImBAweyfPnymPw5pGRixye1obo62LYNdu0KOryuXeGEE8J77TPPPMOUKVMYO3Ysv/zlL2nfvn10i5WSlB2f1IYyMqB3bxg8ONhvL9zQAxgzZgzl5eWkp6dTXFzMypUro1anlMzs+KQEtGLFCq6++mrGjRvHnXfeSX5+fqxLkhKGHZ+UgC6++GI2btxIZWUlJSUlPP/887EuSUoYdnxSglu2bBnTpk3jyiuv5PbbbycnJyfWJUlxzY5PSnDjxo2jvLycrVu3MmTIEP7nf/4n1iVJcc2OT0oSDQ0NLF68mGuvvZbvfe973HrrrWRlZcW6LCnu2PFJSSItLY1vfetblJWVUVZWxrBhwygvL491WVLcMfikJHPiiSfyhz/8geuuu46xY8dyxx13cPDgwViXJcUNpzqlJLZt2zamTp1KRUUFCxcu5Kyzzop1SVLM2fFJSaxXr1489dRTTJkyhfPOO4977rmHuqZ3yZVSgh2flCLefvttJk+eTH19PQ888AB9+vSJdUlSTNjxSSnitNNOY9WqVXz961/n3HPPZc6cOdTX18e6LKnN2fFJKej111+ntLSUgoICFixYwMknnxzrkqQ2Y8cnpaB+/fqxZs0axo4dy9ChQ1mwYAH+DKxUYccnpbiNGzdSWlrKiSeeyLx58+jRo0esS5Kiyo5PSnEDBw7kxRdf5JxzzmHw4ME89NBDdn9KanZ8kg5bv349paWl9OvXj7lz59K1a9dYlyRFnB2fpMOGDh3KSy+9xOmnn05JSQlLly6NdUlSxNnxSQpp3bp1lJaWcs455zBr1iw6deoU65KkiLDjkxTSiBEjKCsro0uXLhQXF7N8+fJYlyRFhB2fpBatWrWKKVOmMGbMGO69917at28f65KkY2bHJ6lF559/PuXl5WRkZFBcXMzKlStjXZJ0zOz4JLXKk08+ydVXX81ll13GnXfeSX5+fqxLklrFjk9Sq1x00UWUl5dTWVlJSUkJa9asiXVJUqvY8Uk6Zo8//jjTpk3jO9/5Drfffju5ubmxLklqkR2fpGN22WWXUV5ezrvvvsuQIUP485//HOuSpBbZ8UmKiMWLF3PNNddw9dVX85Of/ISsrKxYlySFZMcnKSK++c1v8sorr1BeXs4555zDK6+8EuuSpJAMPkkR0717dx5//HF+9KMfMXbsWGbMmMHBgwdjXZbUiFOdkqJi27ZtTJ06lV27drFw4UI+97nPxbokCbDjkxQlvXr14qmnnuKqq67ivPPO4+6776auri7WZUl2fJKi7+2332by5MnU1dXxwAMPcPrpp8e6JKUwOz5JUXfaaaexatUqxo8fz/Dhw5k9ezb19fWxLkspyo5PUpt6/fXXmTRpEnl5eSxYsIBTTjkl1iUpxdjxSWpT/fr1Y/Xq1Vx44YWcffbZzJ8/H3/+Vluy45MUM5s2bWLixIl0796d+fPn06NHj1iXpBRgxycpZgYMGMCLL77IsGHDGDRoEL/97W/t/hR1dnyS4sL69espLS2lb9++3HfffXTt2rXlF737LmzeDJWVkJ8Pp5wCAwZAWlr0C1bCsuOTFBeGDh3K+vXr6du3L8XFxSxZsiT0wPp6WL4cRo+Gfv3g29+Gq6+GK6+EESPgzDNhwQKoqmrT+pU47PgkxZ1169ZRWlrK2WefzezZs+nUqVNwYvt2uOACeP992Lu36TcoKIB27eCPf4SRI9umaCUMOz5JcWfEiBGUlZXRtWtXBg4cyBNPPBFMaw4eDG+/3XzoQXC+ogIuvBBWrmyTmpU47PgkxbVnn32W6aWl/OmTT+hcXU1aax97lp8PL70UTIFK2PFJinOjR4/m5euvp7CmpsnQewPIAa4MdbK6Gn760yhWqERjxycpvjU0QJ8+8M47TQ75IlANnAL8NtSA7Gx47z3o3Dk6NaaYDz+EX/8ann0Wdu2C3Fw49VT4/vfh7/4u/hfV2vFJim9r18LOnU2efgToAFzQ3Hukp8NvfhPZulJQWRl89avBXSM/+xk88wxs2BD8Ez30EFxySRCAc+cGi2/jlcEnKb6tWQP794c8VQn8BPhlS+9RXQ0rVkS4sNTy+9/DqFHwxBPBP0dNTePzDQ3BmqL//V+44Qb48peDv/Z4ZPBJim9//Ss0sYv7rcBU4KQw3qb6/ffZtm0blZWV7gzRSsuXw8SJwa2R4Vwcq6qCVatg3DiIxy0Y28W6AElqVnZ2yMNlwNPAhjDf5s133+WSkSPZvXs3+/bto7CwkKKiolZ/dejQgaKiIgoKCkhPT/7eYedO+MY3mnoewGjgBT6Lkp7A60DQEa5ZA3fdBf/0T21RafgMPknx7cQTg9UTR8ybPQtsBU4+9Pu9QB3wF+DlEG8z8KKL2PbYYwDU1dVRWVnJ7t27m/zatWsXW7dubXSsoqLi8PdVVVUpEZ7z5rV0vW42cFXIM1VVcM89cOONkJERjeqOjas6JcW3Dz6A3r2Pus5XRXCN71N3EwThXOCEI9+jsBAefhguvTRiZYUTnk2FZiTC89MAjWZ41tVB9+7w8cdNjRhNcBNJ6OCD4K/+wQfhK1+JfH3HyuCTFP++8pXg8WPNfFzdBrxJE7cznHAC7NgRX20H4YVnqMBsq/B8+mm4/HLYs6epP8FoYDPQAPQD7jh0rLExY+LrAToGn6T4t3o1XHwx7NvX+tfm5sItt8DNN0e+rjjQXHi2FJothedHH13Gyy9P5ODB0NdZ4UXgc0AWwY0l/0Bw9bVPo1G9ezd7G2abM/gkJYYf/AAeeKB1uy5kZUFxcbDKoolFMgodnhUVFfzudyeyePFgDh4MdznIRcClwA8bHe3WLZixjhcubpGUGGbNCvbdW7o0vPDLyQm2Lfqv/zL0WpCRkUHHjh3p2LFjo+N//SssW9bk3SQhpBFMezZWUHC8FUZWfC8nkqRPpafDokUwYwZ06hSsmgglPz+Y3iwthXXr4IgPc4VvwIDmzlYATwE1wEHgQeBPBF3fZ9LSgk014olTnZISz8GDwSNEfvELePVV6vbupTYtjdzTToNrrw02pW0qGBW2hgbo2xfefDPU2Y+AS4DXgAzgTOB24MJGo/Lz4b//O9gjOF4YfJIS3uLFi1myZAmPPvporEtJOvPnw3XXHdu6IgieL/7GG/H14GqnOiUlvKKiIiorK1seqFb79reDru1YgisvL5iZjqfQA4NPUhIoKipi9+7dsS4jKeXnB7swtHaBSl4eTJ8O3/pWdOo6HgafpITXvn17gy+K+veH558PtjPMzW1+bHp6EHo33hhcgo1HBp+khOdUZ/QNHAhbtsBttwX35RUUNJ7CzMsL7hq5/PJgZ4bbbou/Kc5PubhFUsKrrKykR48e7N27N9alpIT6+uD2yPXrg90bCgqgVy+44orE2OTe4JOU8Orr68nMzGT//v20a+dzOdQ8pzolJbz09HQKCwvZ0/TTlKXDDD5JScEFLgqXwScpKbjAReEy+CQlBe/lU7gMPklJwalOhcvgk5QUnOpUuAw+SUnBjk/hMvgkJQU7PoXL4JOUFFzconAZfJKSglOdCpfBJykpONWpcBl8kpKCHZ/CZfBJSgpe41O4DD5JScGpToXL4JOUFJzqVLgMPklJwY5P4XIjWklJYf/+/RQUFFBbW0taWlqsy1Ecs+OTlBSys7PJyMiguro61qUozhl8kpKG050Kh8EnKWm4wEXhMPgkJQ07PoXD4JOUNLyJXeEw+CQlDac6FQ6DT1LScKpT4TD4JCUNOz6Fw+CTlDTs+BQOg09S0nBxi8Jh8ElKGk51KhwGn6Sk4VSnwmHwSUoadnwKh8EnKWl4jU/hMPgkJQ2nOhUOg09S0nCqU+Ew+CQlDTs+hcMd2CUljfr6ejIzM6mtrSUjIyPW5ShO2fFJShrp6ekUFBTY9alZBp+kpFBXB2vXQmbmN3j44YM8/TRUVMS6KsUjpzolJbQPP4Rf/xr+/d+hthb27dtDbm4e7dplsH8/jB8P//iPMGhQrCtVvDD4JCWs++6D668Pvq+pCT0mIwOys+GLX4RHHgm+V2oz+CQlpBkz4Oc/h6qq8Mbn5kJxMTz3nOGX6rzGJynhPPJI60IPoLoaysvhu9+NXl1KDAafpIRSXx9MbzYdeo8AZwH5QB9g9eEz1dWwfDn85S9RL1NxzOCTlFCefhr27Wvq7H8DPwbuB/YAfwJOazTiwAG4995oVqh45zU+SQll7FhYubKpsyOBqYe+mpabG6wGLSyMcHFKCHZ8khLKunVNnakDXgI+Ak4HTgL+Aag+amRmJmzaFKUCFfcMPkkJo6EhuE4X2ofAAWAJwXW9MmADMOOokWlpsGtXdGpU/DP4JCWMtLTgvrzQcg/9+kPgRKAL8CNgecjRWVmRrk6JwuCTlFA6dGjqTEeC6c20vzmWFnLkwYPQrVtEy1ICMfgkJZQJE5rr1iYDs4CdwC7gXuDLR43q3BkGDIhWhYp3Bp+khPLDH0J6k59ctwLnAH0J7uUbDPxzoxH5+XDjjcG0qVKTtzNISjjnnw+rVwc7MrRWXh7s2AHt20e+LiUGOz5JCWfhwmMLrtzcY3+tkofBJynhnHxy8LDpjh2bW+XZWG5u8MSW8eOjW5vin8EnKSENHAhlZXDBBcFuC6F2XEhPD67pnX46LFsG3/9+W1epeOQ1PkkJb/t2mDMHFi0Kbkyvq4OCAhg9OljIMmyYi1n0GYNPkpRSnOqUJKUUg0+SlFIMPklSSjH4JEkpxeCTJKUUg0+SlFIMPklSSjH4JEkpxeCTJKUUg0+SlFIMPklSSjH4JEkpxeCTJKUUg0+SlFIMPklSSjH4JEkpxeCTJKUUg0+SlFIMPklSSjH4JEkpxeCTJKUUg0+SlFL+D3J+7gv5l/anAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "G = nx.Graph()\n", + "G.add_edges_from(max_cut_graph_edges)\n", + "\n", + "H = nx.Graph()\n", + "H.add_edges_from(max_cut_graph_edges)\n", + "\n", + "plt.figure(1)\n", + "nx.draw(G, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['red', 'blue', 'red','red', 'blue', 'red', 'red'])\n", + "plt.figure(2)\n", + "nx.draw(H, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['blue', 'red', 'blue', 'blue', 'red', 'blue', 'blue'])\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c4f545a-011b-44b5-943d-c520f4cb3483", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "vscode": { + "interpreter": { + "hash": "3289aa74b4cc5b65254d7b081e6c83acb4efa1b1c1d2fe845644451ee4b44b02" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/team_solutions/pineapple/maxcut_plotting.py b/team_solutions/pineapple/maxcut_plotting.py new file mode 100644 index 0000000..e2b6afa --- /dev/null +++ b/team_solutions/pineapple/maxcut_plotting.py @@ -0,0 +1,34 @@ +from pytket.backends.backendresult import BackendResult +import matplotlib.pyplot as plt + + +def plot_maxcut_results(result: BackendResult, n_strings: int) -> None: + """ + Plots Maxcut results in a barchart with the two most common bitstrings highlighted in green. + """ + counts_dict = result.get_counts() + sorted_shots = counts_dict.most_common() + n_shots = sum(counts_dict.values()) + + n_most_common_strings = sorted_shots # [:n_strings] + x_axis_values = [str(entry[0]) + for entry in n_most_common_strings] # basis states + # print(x_axis_values) + y_axis_values = [entry[1] for entry in n_most_common_strings] # counts + # print(y_axis_values) + num_successful_shots = sum(y_axis_values[:2]) + print(f"Success ratio {num_successful_shots/n_shots} ") + + fig = plt.figure() + ax = fig.add_axes([0, 0, 1.5, 1]) + color_list = ["green"] * 2 + (["orange"] * (len(x_axis_values) - 2)) + ax.bar( + x=x_axis_values, + height=y_axis_values, + color=color_list, + ) + ax.set_title(label="Maxcut Results") + plt.ylim([0, 0.25 * n_shots]) + plt.xlabel("Basis State") + plt.ylabel("Number of Shots") + plt.show() diff --git a/team_solutions/pineapple/maxcut_symbolic_compiler.ipynb b/team_solutions/pineapple/maxcut_symbolic_compiler.ipynb new file mode 100644 index 0000000..7c3dfb9 --- /dev/null +++ b/team_solutions/pineapple/maxcut_symbolic_compiler.ipynb @@ -0,0 +1,1113 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3ba3449", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The Quantum Approximate Optimisation Algorithm (QAOA) using TKET.\n", + "\n", + "Callum Macpherson" + ] + }, + { + "cell_type": "markdown", + "id": "45668632", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## The Max-Cut problem" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9456fbeb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "G = nx.Graph()\n", + "G.add_edges_from([(0,1), (1,2), (2,0)])\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, node_color=['red', 'blue', 'red'])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cad41481", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "There are \\\\(2^3\\\\) possible assignments of colour to nodes. In general there are \\\\( 2^n \\\\). The Max-cut problem can then be stated as that of finding the colour assignment which maximises the number of edges between vertices of a different colour." + ] + }, + { + "cell_type": "markdown", + "id": "7389bbe5", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Quantum Approximate Optimization Algorithm (QAOA)\n", + "\n", + "Introduced in 'A Quantum Approximate Optimization Algorithm' (found at https://arxiv.org/abs/1411.4028). The idea is to prepare a quantum state which encodes a solution to the Max-cut problem.\n", + "\n", + "\n", + "This is a variational algorithm, which is to say that a paramaterised state is prepared, with the parameters varied to improve the solution. We will have $2p$ parameters where p is our number of layers. In particular, the state prepared has the form \n", + "\n", + "\n", + "\n", + "\\\\[ \\left| \\psi \\left( \\beta, \\gamma \\right) \\right\\rangle = U \\left( \\beta_m \\right) U \\left( \\gamma_m \\right) ... U \\left( \\beta_0 \\right) U \\left( \\gamma_0 \\right) \\left| \\psi_0 \\right\\rangle \\\\]\n", + "where\n", + "\\\\[ U \\left( \\beta_i \\right) = e^{i \\beta H_B} \\quad \\& \\quad U \\left( \\gamma_i \\right) = e^{i \\gamma H_P} \\\\]\n", + "with \\\\( H_B \\\\) and \\\\( H_P \\\\) depending on the problem instance. " + ] + }, + { + "cell_type": "markdown", + "id": "3596e66d", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "d720b387", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "For the previous 3 vertex graph the *problem Hamiltonian* is\n", + "\\\\[ H_P = \\frac{1}{2} \\big[ \\left( Z \\otimes Z \\otimes I \\right) + \\left( Z \\otimes I \\otimes Z \\right) + \\left( I \\otimes Z \\otimes Z \\right) \\big] \\\\]\n", + "\n", + "\n", + "where you will notice that there is a \\\\( Z \\otimes Z \\\\) acting between each vertex which is connected by an edge." + ] + }, + { + "cell_type": "markdown", + "id": "8ca2e1b9", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "The *mixer Hamiltonian* has the form \n", + "\\\\[ H_B = \\left( X \\otimes I \\otimes I \\right) + \\left( I \\otimes X \\otimes I \\right) + \\left( I \\otimes I \\otimes X \\right) \\\\]\n", + "\n", + "\n", + "where you will notice that there is an \\\\( X \\\\) acting on each vertex." + ] + }, + { + "cell_type": "markdown", + "id": "de6b9e03", + "metadata": {}, + "source": [ + "## Cost function for Maxcut\n", + "\n", + "A solution to maxcut can be found by maximising the following cost function $C$ .\n", + "\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "C= \\sum_{(i,j)} x_i(1-x_j)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here $x_i$ and $x_j$ are the the \"colours\" of each vertex. \n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i,x_j \\in \\{0,1\\}\n", + "\\end{equation}\n", + "$$\n", + "\n", + "$x_i(1-x_j)=0$ if $x_i=x_j$ and $ x_i(1-x_j)=1$ if the terms are not equal." + ] + }, + { + "cell_type": "markdown", + "id": "61d4e798", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We want to encode our Maxcut cost function as a Hamiltonain. To do this we can perform the following translation.\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "x_i \\mapsto \\frac{1}{2}(1-Z_i)\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "The Pauli Z operator can be used to distinguish between the $|0\\rangle$ and $|1\\rangle$ basis states as these are eigenstates with eigenvalues $\\pm 1$ .\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$\n", + "\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "H_B = \\sum_i X_i\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Here we use the the convention that $X_i$ means a Pauli X operator will be applied to the \"ith\" qubit and the identity operator will be applied to all other qubits in the circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "aeb6abd9", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "id": "3cbb783a", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Circuit Construction for QAOA" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "688f1332", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import networkx as nx\n", + "\n", + "max_cut_graph_edges = [(0,1), (1,2), (1,3), (3,4), (4,5), (4,6)]\n", + "n_nodes = 7\n", + "\n", + "max_cut_graph = nx.Graph()\n", + "max_cut_graph.add_edges_from(max_cut_graph_edges)\n", + "nx.draw(max_cut_graph, labels={node: node for node in max_cut_graph.nodes()})\n", + "\n", + "expected_results = [(0,1,0,0,1,0,0), (1,0,1,1,0,1,1)]" + ] + }, + { + "cell_type": "markdown", + "id": "18a5bd16", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define Cost Hamiltonian: $\\gamma H$" + ] + }, + { + "cell_type": "markdown", + "id": "543f87ca", + "metadata": {}, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = \\frac{1}{2}\\sum_{} (-Z_j \\,Z_k +I )\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "99226b24", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(): 3.0*cost0_5, (Zq[0], Zq[1]): -0.5*cost0_5, (Zq[1], Zq[2]): -0.5*cost0_5, (Zq[1], Zq[3]): -0.5*cost0_5, (Zq[3], Zq[4]): -0.5*cost0_5, (Zq[4], Zq[5]): -0.5*cost0_5, (Zq[4], Zq[6]): -0.5*cost0_5}\n" + ] + } + ], + "source": [ + "from typing import List, Tuple, Any\n", + "from pytket.utils import QubitPauliOperator\n", + "from pytket.circuit import fresh_symbol\n", + "from pytket.pauli import QubitPauliString, Pauli\n", + "from pytket import Qubit\n", + "\n", + "cost0 = fresh_symbol(\"cost0\")\n", + "cost1 = fresh_symbol(\"cost1\")\n", + "cost2 = fresh_symbol(\"cost2\")\n", + "mixer0 = fresh_symbol(\"mixer0\")\n", + "mixer1 = fresh_symbol(\"mixer1\")\n", + "mixer2 = fresh_symbol(\"mixer2\")\n", + "vardict = {0: cost0, 1: cost1, 2: cost2, 3: mixer0, 4: mixer1, 5: mixer2}\n", + "\n", + "\n", + "def qaoa_graph_to_cost_hamiltonian(edges: List[Tuple[int, int]], name) -> QubitPauliOperator:\n", + " qpo_dict = {QubitPauliString(): float(len(edges))*0.5*name}\n", + " for e in edges:\n", + " term_string = QubitPauliString([Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z])\n", + " qpo_dict[term_string] = -0.5*name\n", + " return QubitPauliOperator(qpo_dict)\n", + "\n", + "cost_ham_qpo = qaoa_graph_to_cost_hamiltonian(max_cut_graph_edges, cost0)\n", + "print(cost_ham_qpo)" + ] + }, + { + "cell_type": "markdown", + "id": "6da499ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "$$\n", + "\\begin{equation}\n", + "H_P = 3 I^{\\otimes 6} -0.5 \\big[ Z_0 Z_1 + Z_1 Z_2 +Z_1 Z_3 +Z_3 Z_4 +Z_4 Z_5 +Z_4 Z_6 \\big]\n", + "\\end{equation}\n", + "$$\n", + "\n", + "Using the same index convention as above" + ] + }, + { + "cell_type": "markdown", + "id": "785ff56c", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Hamiltonian Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "11fe9917", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.utils import gen_term_sequence_circuit\n", + "from pytket import Circuit\n", + "from pytket.circuit import display\n", + "\n", + "cost_ham_circuit = gen_term_sequence_circuit(cost_ham_qpo, Circuit(n_nodes))\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "9057c55f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.transform import Transform\n", + "\n", + "Transform.DecomposeBoxes().apply(cost_ham_circuit)\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4690b787", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construction of the Mixer Hamiltonian: $\\beta B$" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "296c560d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mixer_angle = 0.8\n", + "mixer_ham_qpo = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer_angle for i in range(n_nodes)})\n", + "mixer_ham_circuit = gen_term_sequence_circuit(mixer_ham_qpo, Circuit(n_nodes))\n", + "Transform.DecomposeBoxes().apply(mixer_ham_circuit)\n", + "display.render_circuit_jupyter(mixer_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "4d128a70", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define the Initial State" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "0a9db628", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit\n", + "\n", + "def qaoa_initial_circuit(n_qubits: int) -> Circuit:\n", + " c = Circuit(n_qubits)\n", + " for i in range(n_qubits):\n", + " c.H(i)\n", + " return c\n", + "\n", + "superposition_circuit = qaoa_initial_circuit(n_nodes)\n", + "Transform.DecomposeBoxes().apply(superposition_circuit)\n", + "backend.default_compilation_pass(2).apply(superposition_circuit)\n", + "\n", + "display.render_circuit_jupyter(superposition_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "da759b59", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Construct QAOA Circuit" + ] + }, + { + "cell_type": "markdown", + "id": "359a1a0f-e92e-40ae-bbe6-ce960b118f49", + "metadata": {}, + "source": [ + "Now lets define a function to create our entire QAOA circuit. For $p$ QAOA layers we expect that our circuit will require $2p$ parameters. Here we will pass and cost mixer parameters in as a list where the length of the list defines the number of layers." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "23f8910a", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_max_cut_circuit(edges: List[Tuple[int, int]],\n", + " n_nodes: int, length: int) -> Circuit:\n", + " \n", + " # initial state\n", + " qaoa_circuit = superposition_circuit.copy() \n", + " # add cost and mixer terms to state\n", + " for index in range(length):\n", + " cost_ham = qaoa_graph_to_cost_hamiltonian(edges, vardict[index])\n", + " mixer_ham = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): vardict[index + length] for i in range(n_nodes)})\n", + " qaoa_circuit.append(gen_term_sequence_circuit(cost_ham, Circuit(n_nodes)))\n", + " qaoa_circuit.append(gen_term_sequence_circuit(mixer_ham, Circuit(n_nodes)))\n", + " \n", + " Transform.DecomposeBoxes().apply(qaoa_circuit)\n", + " Transform.OptimisePhaseGadgets().apply(qaoa_circuit)\n", + " \n", + " return qaoa_circuit" + ] + }, + { + "cell_type": "markdown", + "id": "bc2f8939-41b7-476b-a5a8-09de07211079", + "metadata": {}, + "source": [ + "We also need to extract our energy expectation values from a `BackendResult` object after our circuit is processed by the device/simulator. We do this with the `get_max_cut_energy` function below. Note that the fact that the maxcut Hamiltonian contains only commuting terms means that we do not need to calculate our energy expectation using multiple measurement circuits. This may not the the case for a different problem Hamiltonian." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "df387eea-4198-428e-9b92-4f3bceb12f0e", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple\n", + "from pytket.backends.backendresult import BackendResult\n", + "\n", + "def get_max_cut_energy(edges: List[Tuple[int, int]], results: BackendResult) -> float:\n", + " energy = 0.0\n", + " dist = results.get_distribution()\n", + " for i, j in edges:\n", + " energy += sum((meas[i] ^ meas[j]) * prob for meas, prob in dist.items())\n", + "\n", + " return energy" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "e5abad7b-e989-4156-9708-3d8c97d8ca2a", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket.backends.backend import Backend\n", + "from typing import Callable\n", + "import numpy as np\n", + "\n", + "def qaoa_instance(\n", + " backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " guess_mixer_angles: np.array,\n", + " guess_cost_angles: np.array,\n", + " prep_circuit: Circuit,\n", + " seed: int,\n", + " shots: int = 5000,\n", + ") -> float:\n", + " # step 1: get state guess\n", + " measured_circ = prep_circuit.copy().measure_all()\n", + " symbol_dict = {}\n", + " for i in range(guess_mixer_angles.shape[0]):\n", + " symbol_dict[vardict[i]] = guess_cost_angles[i]\n", + " symbol_dict[vardict[i + guess_mixer_angles.shape[0]]] = guess_mixer_angles[i]\n", + " \n", + " measured_circ.symbol_substitution(symbol_dict)\n", + " res = backend.run_circuit(measured_circ, shots, seed=seed)\n", + "\n", + " return get_max_cut_energy(max_cut_graph_edges, res)" + ] + }, + { + "cell_type": "markdown", + "id": "2c01c28b", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Optimise Energy by Guessing Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "0a44bed8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_optimise_energy(compiler_pass: Callable[[Circuit], bool],\n", + " backend: Backend,\n", + " iterations: int = 100,\n", + " n: int = 3,\n", + " shots: int = 5000,\n", + " seed: int= 12345):\n", + " \n", + " highest_energy = 0 \n", + " best_guess_mixer_angles = [0 for i in range(n)] \n", + " best_guess_cost_angles = [0 for i in range(n)]\n", + " rng = np.random.default_rng(seed)\n", + " # guess some angles (iterations)-times and try if they are better than the best angles found before\n", + " my_prep_circuit = qaoa_max_cut_circuit(\n", + " max_cut_graph_edges, n_nodes, len(best_guess_mixer_angles)\n", + " )\n", + " for i in range(iterations):\n", + " \n", + " guess_mixer_angles = rng.uniform(0, 1, n)\n", + " guess_cost_angles = rng.uniform(0, 1, n)\n", + " \n", + " qaoa_energy = qaoa_instance(backend,\n", + " compiler_pass,\n", + " guess_mixer_angles,\n", + " guess_cost_angles,\n", + " my_prep_circuit,\n", + " seed=seed,\n", + " shots=shots)\n", + " \n", + " if(qaoa_energy > highest_energy):\n", + " \n", + " print(\"new highest energy found: \", qaoa_energy)\n", + " \n", + " best_guess_mixer_angles = np.round(guess_mixer_angles, 3)\n", + " best_guess_cost_angles = np.round(guess_cost_angles, 3)\n", + " highest_energy = qaoa_energy\n", + " \n", + " print(\"highest energy: \", highest_energy)\n", + " print(\"best guess mixer angles: \", best_guess_mixer_angles)\n", + " print(\"best guess cost angles: \", best_guess_cost_angles)\n", + " return best_guess_mixer_angles, best_guess_cost_angles" + ] + }, + { + "cell_type": "markdown", + "id": "d22226cc", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Calculate the State for the final Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "da46e63d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def qaoa_calculate(backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " shots: int = 5000,\n", + " iterations: int = 100,\n", + " seed: int = 12345,\n", + " ) -> BackendResult:\n", + " \n", + " # find the parameters for the highest energy\n", + " best_mixer, best_cost = qaoa_optimise_energy(compiler_pass,\n", + " backend,\n", + " iterations,\n", + " 3,\n", + " shots=shots,\n", + " seed=seed)\n", + " \n", + " # get the circuit with the final parameters of the optimisation:\n", + " my_qaoa_circuit = qaoa_max_cut_circuit(max_cut_graph_edges,\n", + " n_nodes,\n", + " len(best_mixer))\n", + "\n", + " final_circ = my_qaoa_circuit.copy().measure_all()\n", + " symbol_dict = {}\n", + " for i in range(len(best_mixer)):\n", + " symbol_dict[vardict[i]] = best_cost[i]\n", + " symbol_dict[vardict[i + len(best_mixer)]] = best_mixer[i]\n", + " \n", + " final_circ.symbol_substitution(symbol_dict)\n", + "\n", + " compiler_pass(final_circ)\n", + " handle = backend.process_circuit(final_circ, shots, seed=seed)\n", + "\n", + " result = backend.get_result(handle) \n", + " \n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "9dd97e10", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Results with the Noiseless Simulator" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "e7afb38e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "aaea7e2f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new highest energy found: 3.1432\n", + "new highest energy found: 3.283599999999999\n", + "new highest energy found: 4.361\n", + "new highest energy found: 4.925600000000001\n", + "new highest energy found: 4.941999999999999\n", + "highest energy: 4.941999999999999\n", + "best guess mixer angles: [0.392 0.247 0.138]\n", + "best guess cost angles: [0.592 0.738 0.608]\n", + "CPU times: user 4.53 s, sys: 31.1 ms, total: 4.56 s\n", + "Wall time: 4.55 s\n" + ] + } + ], + "source": [ + "%%time\n", + "res = qaoa_calculate(backend, backend.default_compilation_pass(2).apply, shots = 5000, iterations = 100, seed=12345)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "3b86301e-7645-4553-be38-3ebf89eedd37", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success ratio 0.4252 \n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from maxcut_plotting import plot_maxcut_results\n", + "\n", + "plot_maxcut_results(res, 6)" + ] + }, + { + "cell_type": "markdown", + "id": "6e36c4fb-a118-4ab8-be01-77a674f273e3", + "metadata": {}, + "source": [ + "Here the binary strings in the results correspond to the two optimal colourings of our graph." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "ffce2a97-902c-44d8-8d64-b25498752907", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAb4AAAEuCAYAAADx63eqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAAsTAAALEwEAmpwYAAAmsklEQVR4nO3deXBV93338bdAu4RAIHYj9t02YpGQAaONxZgLapo4zRM3i50mbRN3GWcSJ02exmnyNCF24nSavUvqJm4ymcTjC0IumyQEwhiwEdhiB4FYBEJC+76c549jTrhCAkncc8+R7uc1wxjrHh192fS5v+X7OyGGYRiIiIgEiWFOFyAiIhJICj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqCj4REQkqoU4XICIi3Zw9CydPQn09xMbCzJnw8MNOVzVkKPhERNygvR28XtiyBUpKIDwcurogJAQ6OmDaNHjhBfjoRyEy0ulqB7UQwzAMp4sQEQlqZ89CZibU1pqjvN7ExkJEBOzcCUuWBK6+IUbBJyLipBMnYMUKqKuDvn47jokxw2/FCntrG6IGT/BduwbvvWe+I4qMhIcegsWLzWkAEZHBqKoK5s+Hysq+h95tcXFw/DhMnWpPbUOYu9f4DAPy8+Gll6CgwBzi357z7uqCMWPgS1+CT3zC/EsgIjKY/PSn5tRmD6GXDhzkj9+kJwOn77ygqQm++13zHtIv7h3xVVbCunXm3HdDQ+/XxcSY//3DH2D9+sDUJiLyoDo7YcIE83tdD9KBPwf+4l73iI6Gioo/fh+UPnFnH19FhTmNWVJy79ADaGw0f3zoQ/D73wemPhGRB5WbC62tD3aPkBB47TX/1BNE3Bd87e2QlQU3bkBbW98/r7kZPvUpOHzYvtpERPylsPDeOziBrwIJwEqgoKcLGhth1y6/lzbUuS/4vF64eNEMwB78FpgPxAAzgX13vtjUBF/+st0Viog8uIqKe768BbgAXAU+B2wCzvd0YS9TpdI79wXfli29Tm/uAl4AfgnUA4XAjO4XHTxoBqeIiJvdpwl9OTACiAA+hTnqyx3AfeRu7gq+kyfNdb1efAP4RyAVs/DJH/zw0dUFP/qRXRWKiPhF50MP0RXa9431IcBdOxFDQiAx0Z9lBQV3BV9RUa99eZ3AEeAmMAt4CHgOaO5+YVsb7NhhY5EiIgNTW1vLb3/7Wz7+8Y+T/PLLtHd29nhdDbADaAE6gNcwZ7ie6H5hdDQ884x9BQ9R7gq+mppe1/ZuAO3A7zHX9YqBo8C3e7q4ttaW8kRE+qusrIwf//jHrFu3jilTpvCrX/2K9PR0ck6eJKKXk1faga8DYzE3t/wr8AYwp/uFEyfC8uX2FT9EuauBPSwMhvWcxVEf/PdvgIkf/Px5zOD7fz3dR0TEAYZhUFxczNatW/F6vZSVlbFx40b+8i//ktdff53Y2Ng/XvyVr8DHPmbuzrzDWOC++9NjYszNfDq9qt/cFXwTJpgnkvfQ2xKPOb155x9xb3/cZe3tXCgoIDU1lUgt/IqIzdra2ti7dy9er5etW7cSHh5OdnY2r7zyCitXriS0t7W8jRvNHuTXXzd3pfdVZKR5Tuezz/rnFxBk3HVyS309jB9v9uT14B+BN4HtQBiwGfN0g2/dcU1HVBR/yMjgB5WVlJSUkJycTHp6Ounp6aSmphIREWHzL0JEgkFNTQ1vvvkmXq+XHTt2MHfuXLKzs9m8eTMLFiwgpK8jsfZ2+LM/Mw+d7jby61F0NCxbZjbA68SWAXFX8AF85jPw6qvmcT7dtAN/B/wPEAl8FPjeBz+3xMbCzZsQGUldXR379++noKCAgoICTpw4QUpKihWEy5cvVxCKSJ9dunTJmsI8dOgQq1evJjs7G4/Hw8SJE+9/g950dcH3vme2c3V29tzYfnuK9AtfgG99S0s6D8B9wVdSAsnJvY767ikiAv7qr+CHP+zx5draWp8gPHnyJMuXL7eCMCUlRUEoIhbDMDh69Cherxev18vVq1fZuHEj2dnZrFu3jhh/j7ja22HrVvNg/rNnzenPqCjzCQzPPw9PPaW+PT9wX/ABfPOb5ruf/sx5h4bCzJlw5Mgf3xndR21tLfv27bOC8PTp03cFYXh4+AB/ESIyGLW1tVFQUGCt10VGRpKdnU12djYrVqxg+PDhTpcoD8idwWcY5rubX/yib+EXEQFTpsC+feYGmQGqqanxCcIzZ86QmppqBWFycrKCUGQIqq6uJjc3l61bt7Jjxw4WLFjA5s2byc7OZt68eX1fr5NBwZ3Bd9t//id87Wvmgm9Pc97R0ebc+FNPmae1+PmZfNXV1T5BePbsWR577DHS09PJyMhg2bJlhGmeXWRQunjxojWqO3z4MOnp6WzevBmPx8OEB3gDLe7n7uADM9h27jSnPouLzRFgWJi5+/Pznze3844aFZBSqqurKSwstILw/PnzPkG4dOlSBaGISxmGwTvvvGOFXXl5OR6Ph+zsbNasWeP/9TpxLfcHn4vdunXLJwgvXLjAihUrrCBcsmSJglDEQa2treTn5+P1etm2bRsxMTHWel1qaqrW64KUgs+PqqqqfIKwtLSUlStX+gRhr42sIuIXt27dIjc3F6/Xy65du1i4cKEVdnPnznW6PHEBBZ+NKisrfYLw0qVLPkG4ePFiBaGIH5SWllotB++88w4ZGRlkZ2ezceNGxo8f73R54jIKvgCqrKxk7969VhCWlZWxatUqKwiTkpIUhCJ90NXVZa3Xeb1eKioqfNbroqOjnS5RXEzB56CbN2/6BOGVK1dYtWoVGRkZpKenk5SUpDUIkQ+0tLT4rNfFxcVZLQfLly/XvxXpMwWfi1RUVPgE4dWrV3n88cetIFy0aJH+cUtQqaqq8lmve/TRR62wmzPnrof0iPSJgs/Fbty44ROE5eXlPkH46KOPKghlyDl//rzVcvDuu++SlZVl9deNHTvW6fJkCFDwDSLXr1/3CcIbN27cFYTDenmeoYhbdXV1cfjwYSvsKisr2bRpE5s3b2bNmjVERUXd/yYi/aDgG8SuX79uhWBBQQEVFRWsXr3aCsJHHnlEQSiu1NLSwp49e6z1uvj4eKvlICUlRX9vxVYKviHk2rVrPiPCyspKnyB8+OGH9Q1FHFNZWcn27dvxer3s2bOHRYsWWc+vmz17ttPlSRBR8A1hV69e9QnCW7du+QThwoULFYRiq3PnzlktB8eOHSMrK8vqr0tISHC6PAlSCr4gcuXKFZ8grKmpIS0tzXr6xIIFCxSE8kC6uro4dOiQFXbV1dVs2rSJ7OxssrKyiNSz5MQFFHxB7PLlyz5BWFdXd1cQ6nEscj/Nzc0+63UJCQlWy0FycrLeTInrKPjEUlZW5hOE9fX1Vgimp6czf/58BaEA5uELt9fr8vLyWLx4sRV2M2fOdLo8kXtS8EmvLl265BOEjY2NPkGoB3QGlzNnzlgtB8ePH2ft2rVs3ryZjRs3MmbMGKfLE+kzBZ/02cWLF60gzM/Pp6WlxScI586dqyAcQjo7O3n77betsKutrWXz5s1s3ryZzMxMrdfJoKXgkwG7ePGiNRrMz8+nra3NJwjnzJmjIBxkmpqa2L17N16vl5ycHMaNG2e1HCxbtkzrdTIkKPjELwzDuCsIOzo6fIJw9uzZCkIXqqioICcnB6/XS35+PkuXLrXCbsaMGU6XJ+J3Cj6xhWEYlJaW+gRhV1eXTxDOmjVLQeiQ06dPWy0HJSUlrF27luzsbJ588klGjx7tdHkitlLwSUAYhsGFCxd8ghDwCcKZM2cqCG3S2dnJwYMHrbBraGiwdmFmZGQQERHhdIkiAaPgE0cYhsH58+d9gnDYsGE+QThjxgwF4QNoampi586dbN26lZycHCZOnGiF3dKlS/V7K0FLwSeuYBgG586d8wnC0NBQnyCcPn26vlnfx40bN6z1uoKCApKTk62dmNOnT3e6PBFXUPCJKxmGwdmzZ32CMDw83ArBjIwMpk2b5nSZjjMMg1OnTrF161a8Xi8nTpxg/fr1ZGdns2HDBuLj450uUcR1FHwyKBiGwZkzZ3yCMDIy0icIp06dGvC6GhrgjTfg8mVobIRRo2DxYsjMBLsGp52dnRw4cMDqr2tubrZGdenp6VqvE7kPBZ8MSoZhcPr0aZ/nEUZFRVkhmJ6eTmJiom1f/+RJeOUV+PWvYfhwaG6Gzk4IC4OICIiLgy9+EZ591gzDB9XY2MjOnTvxer1s376dyZMnWy0HS5Ys0RSwSD8o+GRIuD3ld2cQxsTE+AThlClT/PK1fvxj+NKXoL0dOjp6vy46GqKiID8fHnmk/1/n+vXrbNu2Da/XS2FhISkpKVbYOTG6FRkqFHwyJBmGwcmTJ61p0YKCAuLi4nyC8KGHHur3fV96CV58EZqa+v45I0ZAUdH9w+92zbdbDk6fPu2zXjfKH0NHEVHwSXAwDIMTJ05YQbh3715GjhzpE4STJ0++5z127IAPfcic1uyvhAQ4f96cAr1TR0eHtV7n9Xppa2uzWg7S0tIIDw/v/xcTkXtS8ElQ6urquisI4+PjrSBMS0u7KwiXLoV3373XXc8CjwAfAX7t80p0NGzZAs89Bw0NDT7rdYmJiVbYJSUlab1OxGYKPhHMICwpKfEJwjFjxlhBOGFCFhs3jrvPaG8d0AxMpXvwAcTH15Oa+jH2799HamqqtRPTzk04InI3BZ9ID7q6unj//fetIMzNzaat7WkgrJfP+C3wOrAAOEdPwRca2sw//MMBnn9+GSNHjrStdhG5NwWfSB8kJRkcO9bbFGQdsAzIA/6d3oIvMtLcHPPcc7aVKSJ9oIdrifRBXd291t3+L/AZ4N67RNvaoLbWn1WJyECEOl2AyGAQFdXbK8XAbuDofe8RGmpuchERZyn4RPpgxgw4caKnVwqAi8DtDSoNQCdwAvDdAhoRAdrHIuI8rfGJ9MGOHfCRj5hnc/pqwlzju+1lzCD8KTDW58oRI6CiwlzrExHnaMQn0gdr10JsbE/BF/3Bj9tigUi6h154OHz2swo9ETfQiE+kj374Q/ja1/p3XNltUVFQUgJ6JJ6I87SrU6SP/vZvIS3tXhtdehYdDT//uUJPxC0UfCJ9NGwYvP46pKf3fXdmVJT5+KJPfMLW0kSkHxR8Iv0QGQk5Oea5m5Mnm+t+3YWGdhIZCatXm5tiPve5wNcpIr3TGp/IABmG+ay9n/wESkvNtb+GhiuMH3+e3/0ujRkznK5QRHqi4BPxo9zcXH7wgx+we/dup0sRkV4o+ET8qLy8nIcffpjKyko9XkjEpbTGJ+JHEydOJCwsjMuXLztdioj0QsEn4meLFy/m6NH7n90pIs5Q8In4mYJPxN0UfCJ+puATcTcFn4ifKfhE3E3BJ+JnM2bMoLa2lqqqKqdLEZEeKPhE/GzYsGEsWrRIoz4Rl1LwidhA050i7qXgE7GBgk/EvRR8IjZQ8Im4l44sE7FBe3s7I0eO5ObNm8TExDhdjojcQSM+ERuEhYUxf/58jh8/7nQpItKNgk/EJpruFHEnBZ+ITRYvXkxxcbHTZYhINwo+EZtoxCfiTtrcImKThoYGxo8fT01NDWFhYU6XIyIf0IhPxCaxsbFMmTKFU6dOOV2KiNxBwSdiI013iriPgk/ERgo+EfdR8InYSMEn4j7a3CJio8rKSmbNmkV1dTUhISFOlyMiaMQnYquEhARGjBhBaWmp06WIyAcUfCI203SniLso+ERspuATcRcFn4jNFHwi7qLgE7GZgk/EXRR8IjZLTEykpaWFGzduOF2KiKDgE7FdSEiIRn0iLqLgEwkABZ+Ieyj4RAJAwSfiHgo+kQBISkpS8Im4hI4sEwmAjo4ORo4cSXl5OXFxcU6XIxLUNOITCYDQ0FAefvhhjh075nQpIkFPwScSIFrnE3EHBZ9IgCj4RNxBwScSIAo+EXfQ5haRAGlubmb06NHU1NQQERHhdDkiQUsjPpEAiYqKYubMmZSUlDhdikhQU/CJBJCmO0Wcp+ATCSAFn4jzFHwiAaTgE3GeNreIBFB1dTWJiYnU1NQwfPhwp8sRCUoa8YkEUHx8PAkJCZw7d87pUkSCloJPJMA03SniLAWfSIAp+EScpeATCTAFn4izFHwiAXY7+LSvTMQZCj6RAJs0aRIhISFcvXrV6VJEgpKCTyTAQkJCNN0p4iAFn4gDFHwizlHwiThAwSfiHAWfiAMUfCLO0ZFlIg7o6upi5MiRXCotZXR0NERFQUiI02WJBAWN+EQCrbycYd/8JmUtLcSPGwdxcRAaCrNmwc9+Bg0NTlcoMqRpxCcSKLW18Mwz8Oab5v+3tNx9TUwMdHXBF74A3/0u6CBrEb9T8IkEQnk5rFwJ165Ba+v9r4+OhlWrYNs2CA+3vz6RIKKpThG7NTRARgZcvty30ANoaoJ9++Dpp0HvTUX8SsEnYrctW+DSJejo8PnwnwMTgThgDvDv3T+vudmcFt2xIyBligQLTXWK2Km9HcaNg5qau14qAWYBEcApIB3YDiztfmFGBuTl2VqmSDDRiE/ETlu3Qmdnjy8txAw9gJAPfpzv6cK33oKLF+2oTiQoKfhE7PTqq1Bf3+vLnweigXmY055P9nSRYcAbb9hRnUhQUvCJ2Km8/J4v/wSoB/YBf8ofR4A+WluhosLvpYkEKwWfiJ16mea803BgFXAF+GlvF7W3+68mkSCn4BOx05gxfb60g17W+EJDISHBXxWJBD0Fn4id/uRPzNNYuqkAfgs0AJ3ADuA3QFZP9wgPh6weXxGRAVA7g4id6upgwgSzJ+8ON4GPAMeALmAq8LfAZ3u6x/z5cOKEzYWKBA+N+ETsFBcHH/vYXWdujgX2AjVAHfAevYReTAy88ILNRYoEF434ROxWVgaLFvXYxH5PYWEwZw4cOQKRkbaUJhKMNOITsVtiIuzaBSNG9P2Ze+Hh5hRpXp5CT8TPFHwigbBsmXkCy6RJZgD2Ztgw88kMS5dCcbF53JmI+JWCTyRQFi40D6v+zW9g9WqIiDDXAEeOpH7YMLrCw+GjH4WCAjhwAEaPdrpikSFJa3wiTikvhytXoLGRr/zzP7P0qad46rM9bnERET8KdboAkaA1caL5A4gsKOC9y5d5yuGSRIKBpjpFXGDu3LmcOnXK6TJEgoKCT8QF5s2bx+nTp50uQyQoaI1PxAUaGxsZO3Ys9fX1DO/W7C4i/qURn4gLxMTEkJCQQFlZmdOliAx5Cj4Rl9B0p0hgKPhEXGLevHna4CISAAo+EZfQzk6RwFDwibiEpjpFAkPBJ+ISmuoUCQwFn4hLTJo0iYaGBmpra50uRWRIU/CJuERISAhz587VdKeIzRR8Ii6i6U4R+yn4RFxEOztF7KfgE3ER7ewUsZ+CT8RFNNUpYj8dUi3iIs3NzYwePZr6+npCQ/W4TBE7aMQn4iJRUVFMnDiR0tJSp0sRGbIUfCIuo3U+EXsp+ERcRjs7Reyl4BNxGW1wEbGXgk/EZTTVKWIvBZ+Iy2jEJ2IvBZ+Iy4wbN4729naqqqqcLkVkSFLwibhMSEiIpjtFbKTgE3EhTXeK2EfBJ+JCamkQsY+CT8SFNNUpYh8Fn4gLaapTxD46pFrEhdra2oiLi6Ouro7w8HCnyxEZUjTiE3Gh8PBwEhMTuXDhgtOliAw5Cj4Rl9J0p4g9FHwiLqWdnSL2UPCJuJR2dorYQ8En4lKa6hSxh3Z1irhUZWUls2fP5tatW4SEhDhdjsiQoRGfiEslJCQwfPhwbt686XQpIkOKgk/ExTTdKeJ/Cj4RF9POThH/U/CJuJh2dor4n4JPxMU01Snifwo+ERfTVKeI/6mdQcTF2tvbiYuLo7q6msjISKfLERkSNOITcbGwsDCmT5/OuXPnnC5FZMhQ8Im4nKY7RfxLwSfictrZKeJfCj4Rl9POThH/UvCJuJymOkX8S7s6RVyuurqaqVOnUltbq8OqRfxAIz4Rl4uPjyc6Opry8nKnSxEZEhR8IoOApjtF/EfBJzIIaGeniP8o+EQGAe3sFPEfBZ/IIKCpThH/CXW6ABG5t5YWOHVqGUVFz5CdDVFRMHUqfPKTsHCh09WJDD5qZxBxqatX4Qc/gH/7NzAMg4aGP7YyDB8O4eEwfz585SvwkY+AOh1E+kbBJ+JChw/DunXQ1ARtbfe+NiYGNmyA114zw1BE7k3BJ+IyxcWwahU0Nvb9c6KiICsLvF4YppV7kXvSPxERF2luhrVrewq9HwHLgAjg0z1+Xl4ebNlie4kig56CT8RFfvc7czPL3SYBXwee7fVzm5rgpZegvd2m4kSGCAWfiIts2QINDT298qfAnwBj7vn5HR2wbZv/6xIZShR8Ii5x7BiUlT3YPerr4fvf9089IkOVgk/EJc6cMdsUHtTZsw9+D5GhTMEn4hINDdDZ+eD3aW5+8HuIDGUKPhGXiI31z4gvKurB7yEylCn4RFygtbWVqqq3aGpq7eWKDqAF6PzgR8sHH7tbS8t7vPjii+zbt4+2+3W/iwQhBZ+IQ27cuMEvf/lLPvzhDzNu3Dj++7+/SHx8fS9XfxuIAr4L/PqDn3/7rqtiYw3+7u86aG5u5vnnn2fMmDGsX7+eLVu2cOTIETr9MZcqMsjp5BaRADEMg+LiYnJycsjJyeH06dOsW7cOj8fDhg0bGDt2LK++Cs8911tLw/3Fx0NFBYR+cPx8dXU1e/fuJS8vjz179nDt2jXS0tLIysoiMzOTBQsWEKJDPiXIKPhEbNTU1MSePXvIyclh+/btREVFsWnTJjweD6tWrSK82+Gazc2QmAiVlf3/WtHR8PWvw1e/2vs1169fJz8/3wrCpqYmMjIyrCCcPn26glCGPAWfiJ+VlZWxfft2cnJyKCwsZNmyZXg8HjweD3Pnzr3v5x87Zp7V2Z9R30DP6iwtLSU/P589e/aQl5dHRESEFYIZGRlMmjSp7zcTGSQUfCIPqLOzk8OHD7Nt2zZycnK4evUqGzZswOPxsH79ekaNGtXve/b36QxPPgm//vWDPZ3BMAxOnTpljQYLCgqYMGECmZmZZGZmkp6ezujRowf+BURcQsEnMgB1dXXs3LmTnJwccnNzGT9+vDWqS01NZbgf+hKuXTOfx/eLX4Bh+I4AQ0MhLAwWLIAXXrDneXydnZ0cO3bMGg0WFRUxe/ZsKwgff/xxYmNj/ftFRQJAwSfSR+fOnbM2prz99tusWrUKj8fDxo0bmTZtmm1ft7UVfv97yM+HmzedewJ7W1sbhw8ftoLwyJEjJCUlWUH42GOPEREREbiCRAZIwSfSi/b2doqKiqywq62ttUZ1WVlZQT/aaWpqoqioiLy8PPLy8jhx4gSpqalkZmaSlZXFkiVLCL29vVTERRR8IneoqqrizTffJCcnhx07djBz5kw8Hg+bNm1i8eLFDNNTXntVU1NDYWGhFYRlZWWsXr3aCsKFCxfq909cQcEnQc0wDEpKSqxR3fHjx8nMzMTj8fDkk09qV+MDuHHjBgUFBdZmmbq6Op/WiZkzZ6p1Qhyh4JOg09LSwt69e61dmIZhWL116enpREZGOl3ikHTp0iWfHsLhw4dbo8HMzEwmT57sdIkSJBR8EhTKy8vJzc0lJyeHvLw8HnnkEWu9buHChRp5BJhhGJw5c8YKwfz8fBISEqwQTE9PJyEhwekyZYhS8MmQ1NXVxdGjR60pzHPnzrF+/Xo8Hg9PPPGEvqm6TFdXF8ePH7eCcP/+/cyYMcOndSIuLs7pMmWIUPDJkNHY2Mju3but48FGjBhhTWGuXLmSsLAwp0uUPmpvb+fIkSNW68ShQ4d49NFHrSBcsWKFpqRlwBR8MqhdunTJGtXt37+flJQUq7duzpw5TpcnftLc3Mxbb71lBeH7779PSkqKFYTJyclqnZA+U/DJoNLZ2cnBgwetsLt+/TpPPvkkHo+HdevWMXLkSKdLlACoq6vzaZ0oLS3l8ccftzbLPPLII2qdkF4p+MT1ampqrOPB3nzzTSZNmmRtTElJSfHL8WAyuN28edNqncjLy6OqqoqMjAwrCGfPnq0NTGJR8IkrnTlzxhrVHTlyhMcff9yawkxMTHS6PHG5y5cv+7ROGIbh0zoxZcoUp0sUByn4xBXa2trYv3+/FXaNjY3WqC4zM5OYmBinS5RByjAMzp07Z40G8/LyGDVqlBWE6enpjBs3zukyJYAUfOKYmzdvWseD7dq1izlz5lhhl5SUpKkpsUVXVxfvv/++FYKFhYUkJiZao8HVq1drrXiIU/BJwBiGwXvvvWeN6kpKSsjKyrKOB5swYYLTJUoQ6ujo4J133rGmRd9++20WLlzo0zoRHR3tdJniRwo+sVVLSwv5+fnW8WDDhw+3euvS0tL0GBtxnZaWFg4ePGi1Thw7dozk5GQrCFNSUtQTOsgp+MTvrl27xvbt28nJySE/P5+kpCRrCnP+/PmawpRBpb6+nv3791tBeO7cOVatWmUFYVJSklonBhkFnzywrq4u3nnnHWsKs7S0lCeeeMI6Hmz06NFOlyjiN1VVVT6tExUVFaSnp1ubZebOnas3dy6n4JMBqa+v9zkeLD4+3npu3YoVK3SKhgSNq1ev+rROtLe3W6PBrKwspk6d6nSJ0o2CT/qstLTUGtUdOHCA1NRUq7du1qxZTpcn4jjDMLhw4YJP60RsbKwVghkZGYwfP97pMoOegk961dHRwVtvvWWF3c2bN9m4cSMej4e1a9fqtHyR+7j9oOPbIbh3714mT55sBWFaWhqjRo1yusygo+ATH9XV1ezYscM6HiwxMdHamJKcnKxFfJEH0NHRwdGjR60gPHDgAPPmzbN6CFeuXKnDGgJAwRfkDMPg9OnT1qju3XffJS0tzeqt09FOIvZpbW3l7bffttYHjx49ytKlS601wuXLlxMeHu50mUOOgi8ItbW1UVhYaIVda2urNarLyMhQs66IQxoaGigqKrJaJ86cOcOKFSusIFy8eLEOZfcDBV+QqKioIDc3l5ycHHbv3s28efOssFu0aJG2X4u4UHV1tU/rRHl5OWlpaVYQLliwQP92B0DBN0QZhsGxY8esUd2pU6dYs2YNHo+HDRs2aGeZyCBUXl5utU7k5eXR1NRkhWBmZiYzZsywvwjDgPx8ePllOHwYGhshLAzGj4fPfx4+/Wlw+YYdBd8Q0tzcTF5eHtu2bWP79u2Eh4dbx4OtXr1aawUiQ0xpaalP60RkZKRPEE6cONG/X/B//gdeeAFqaqCh4e7Xo6Ohqwueegp+9CNw6c5vBZ8/GAYcOQLXrkFrK4wcCYsXQwAedXLlyhXreLC9e/eyZMkSawpTJ0iIBA/DMDh58qQVggUFBUyYMMGndWLApygZBnz96/DDH0JT0/2vj4iAhx6CffvA3+HrBwq+B1FTA//1X+aQv7YWhg0z/4IMG2YG4Lp18KUvwcqV4KcA6urq4vDhw9YUZllZGRs2bMDj8bB+/Xri4+P98nVEZHDr7OykuLjYCsKioiJmz55tBeGqVauIjY3t281eeglefLFvoXdbaChMmwbvvgsjRgzkl2AbBd9AvfEGPP20+fPe/jKEhJhD/6Qk2L7dHAkOQF1dHbt27SInJ4fc3FzGjh1rjepSU1N1PJiI3FdbWxuHDh2ygvDIkSMkJSVZPYSpqak9Py3l7FlYtAiam30+3Ap8HtgN3AJmAt8BNtx5UUQE/MVfmNOeLqLgG4hXX4W//uu7/iL06vaw/8iRPi/6nj9/3hrVHTx4kBUrVljHgwVkAVtEhrSmpiaKioqsHsKTJ0/y2GOPWeuDS5YsMd9U/83fwM9/Du3tPp/fCLwEfBpIBHKB/wO8B0y788KYGKioMAcBLqHg66/CQnjiib6H3m3h4ea634ED5lRoNx0dHRQVFVlhV11dbR0PtmbNGka4bKpARIaWmpoaCgsLrR7CK1eusGbFCl7bvZvwtrY+3eNR4BvAh+/8YGws/Mu/wLPP2lD1wCj4+mvpUnPOuptbwGeAnUAC5pD/490vio2F11+HtWvNz7l1i//93/9l27Zt7Nixg+nTp1tTmEuXLtXxYCLimBs3bnDme99j2b/+K1HdRns9Xg9MBYqBed1fTE6GQ4f8XuNAaXGoP0pK4OTJHl/6AhCO+YdfDGwEFgEL77jGaGig8Rvf4CdHj5KTk0NxcTEZGRl4PB5efvllJk+ebPMvQESkb8aPH8/42bPNTSr3Cb524GngU/QQegDXr/u/wAeg4OuPV16BHob8jcAfgPeBWGAVsBn4FfDdO64LAULfeou6WbP46le/Snp6OlFRUfbXLSIyEG1tZl/ePXQBn8B849/rFpY+jBgDScHXH3v2QGfnXR8+g/kbOeeOjy0C9vZwi4gRI/i2xwMbNvTwqoiIi4waZZ7K0tra48sG5hLPDczNLWG93cdljewKvv6or+/xww1A9z/WkUBPV4d0dkJ1tZ8LExGxwWOPQUdHry//NXASs6Wh17mrsDDIyvJ/bQ9Auyf6o5d+uVigrtvH6oAe92GGhJjtDSIibjd7trkbvQeXgJ9j7mmYgPl9MBZ4rfuFoaHw939vW4kDoeDrj7Fje/zwHKADOHvHx47hu7HFMny4eZiriMhg8MIL5o70bqZiTnW2YM563f7xdPcLFy2COXO6f9RRCr7++NznemzCjAH+FPhHzI0uRYAXc8H3LoYBmZk2Fiki4kcbN8KMGeaUZX9FRZlHOrqMgq8/PvnJXnc4/QRoBsZhnl7wU3oY8YWHm+GpqU4RGSxCQ2H3bpgwwfwe1ldRUeaJLytX2lfbAKmBvb+eecZ8NEcfTzLwERlp9gFOm+b3skREbFVZCevXw5kzPT+S6Lbbs2K/+Q1s3hyY2vpJI77++v73zXc+/T1VJToavvMdhZ6IDE4JCeZ5w2+8YT55JiLCbFMYMcL8b2wsTJoE3/oWXLni2tADjfgG5tIlWLXKPHi1LyO/6Gj44hfhn/7J/tpERALh6lUoLjYfzxYVBZMnQ0qK3x7BZicF30BVVZlPaNi61Rz99XRodWys+U7opZfg43ed3CkiIg5Q8D2oykr4j/+An/3M/Hl7uznCS06GL3/Z3ME5CN4BiYgECwWfiIgEFW1uERGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoKLgExGRoPL/Aed7qqQ7XIdfAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "G = nx.Graph()\n", + "G.add_edges_from(max_cut_graph_edges)\n", + "\n", + "H = nx.Graph()\n", + "H.add_edges_from(max_cut_graph_edges)\n", + "\n", + "plt.figure(1)\n", + "nx.draw(G, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['red', 'blue', 'red','red', 'blue', 'red', 'red'])\n", + "plt.figure(2)\n", + "nx.draw(H, labels={node: node for node in max_cut_graph.nodes()}, node_color= ['blue', 'red', 'blue', 'blue', 'red', 'blue', 'blue'])\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f129e390-f926-497d-a2ba-88bdc6d07378", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "vscode": { + "interpreter": { + "hash": "3289aa74b4cc5b65254d7b081e6c83acb4efa1b1c1d2fe845644451ee4b44b02" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/team_solutions/pineapple/minimum_dominating_set.ipynb b/team_solutions/pineapple/minimum_dominating_set.ipynb new file mode 100644 index 0000000..f9bf261 --- /dev/null +++ b/team_solutions/pineapple/minimum_dominating_set.ipynb @@ -0,0 +1,825 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3b602e8-f1ca-4f06-9079-5000fbbaf8f8", + "metadata": {}, + "source": [ + "# Minimum Dominating Set Problem" + ] + }, + { + "cell_type": "markdown", + "id": "1a93c695-d39f-4651-82fe-4b87bc3e2156", + "metadata": {}, + "source": [ + "## Graph Construction" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f1a871da-89ec-4ff9-a074-5b5a40627a27", + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "from collections import defaultdict" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "60ae8e92-fe73-47bf-9923-ce90dfea13aa", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# smaller testing graph\n", + "graph_edges = [(0,1), (1,2), (1,3)]\n", + "adj = defaultdict(lambda:[])\n", + "for (i,j) in graph_edges:\n", + " adj[i].append(j)\n", + " adj[j].append(i)\n", + " \n", + "for i in adj.keys():\n", + " adj[i].append(i) # connection to itself\n", + "N = len(adj)\n", + "dominating_set = [1]\n", + "G = nx.Graph()\n", + "G.add_edges_from(graph_edges)\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, node_color=[\"red\" if x in dominating_set else \"blue\" for x in range(len(adj.keys()))])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "id": "4e4449ae-93f9-426f-a575-c72167a65479", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# bigger graph\n", + "graph_edges = [(0,1), (1,2), (1,3), (3,4), (4,5), (4,6), (5, 7), (1, 7)]\n", + "adj = defaultdict(lambda:[])\n", + "for (i,j) in graph_edges:\n", + " adj[i].append(j)\n", + " adj[j].append(i)\n", + " \n", + "for i in adj.keys():\n", + " adj[i].append(i) # connection to itself\n", + "N = len(adj)\n", + "dominating_set = [1, 4]\n", + "G = nx.Graph()\n", + "G.add_edges_from(graph_edges)\n", + "plt.figure(figsize=(2,2))\n", + "nx.draw(G, node_color=[\"red\" if x in dominating_set else \"blue\" for x in range(8)], labels={node: node for node in G.nodes()})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "682e7535-41c4-4519-966e-c35de1e05cdb", + "metadata": {}, + "source": [ + "## Cost Hamiltonian" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f010b143-c398-458f-9c1a-6473de5ca3d4", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple, Any, Dict\n", + "from pytket.utils import QubitPauliOperator\n", + "from pytket.pauli import QubitPauliString, Pauli\n", + "from pytket import Qubit" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "efd6691f-f389-4c4b-9d1d-55294a4f4b8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{(): -1.81250000000000, (Zq[1]): -0.562500000000000, (Zq[0]): -0.0625000000000000, (Zq[0], Zq[1]): -0.312500000000000, (Zq[2]): -0.0625000000000000, (Zq[0], Zq[2]): -0.0625000000000000, (Zq[3]): -0.0625000000000000, (Zq[0], Zq[3]): -0.0625000000000000, (Zq[2], Zq[3]): -0.0625000000000000, (Zq[0], Zq[2], Zq[3]): -0.0625000000000000, (Zq[1], Zq[2]): -0.312500000000000, (Zq[0], Zq[1], Zq[2]): -0.0625000000000000, (Zq[1], Zq[3]): -0.312500000000000, (Zq[0], Zq[1], Zq[3]): -0.0625000000000000, (Zq[1], Zq[2], Zq[3]): -0.0625000000000000, (Zq[0], Zq[1], Zq[2], Zq[3]): -0.0625000000000000}\n" + ] + } + ], + "source": [ + "def qaoa_graph_to_cost_hamiltonian(adj:Dict[int, List[int]], cost_angle: float) -> QubitPauliOperator:\n", + " qpo_dict = defaultdict(lambda:0)\n", + " N = len(adj.keys())\n", + " for node in range(N):\n", + " for i in range(1<<(len(adj[node]))):\n", + " qubits = []\n", + " for j in range(len(adj[node])):\n", + " if (i&(1<\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.utils import gen_term_sequence_circuit\n", + "from pytket import Circuit\n", + "from pytket.circuit import display\n", + "\n", + "cost_ham_circuit = gen_term_sequence_circuit(cost_ham_qpo, Circuit(N))\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "715d357e-da28-468a-aa24-b9385f9e8009", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from pytket.transform import Transform\n", + "\n", + "Transform.DecomposeBoxes().apply(cost_ham_circuit)\n", + "display.render_circuit_jupyter(cost_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "43b9166e-a355-488b-98d3-be988f52fe98", + "metadata": {}, + "source": [ + "## Mixer Hamiltonian" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "1f82803b-15c2-4486-8bf6-461c59dd535f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mixer_angle = 0.8\n", + "mixer_ham_qpo = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer_angle for i in range(N)})\n", + "mixer_ham_circuit = gen_term_sequence_circuit(mixer_ham_qpo, Circuit(N))\n", + "Transform.DecomposeBoxes().apply(mixer_ham_circuit)\n", + "display.render_circuit_jupyter(mixer_ham_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "3d298d00-124f-46de-b2c0-d58dbf9dd937", + "metadata": {}, + "source": [ + "## Initial Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "51d87382-6263-49c7-b0d6-f74cf4c9077c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def qaoa_initial_circuit(n_qubits: int) -> Circuit:\n", + " c = Circuit(n_qubits)\n", + " for i in range(n_qubits):\n", + " c.H(i)\n", + " return c\n", + "\n", + "superposition_circuit = qaoa_initial_circuit(N)\n", + "\n", + "display.render_circuit_jupyter(superposition_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "ddb7438f-34a5-4d7a-815b-283252d3f7f2", + "metadata": {}, + "source": [ + "## Putting it all together" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ab0e4675-f644-4227-82ce-f2bd3ea592ad", + "metadata": {}, + "outputs": [], + "source": [ + "def qaoa_dominating_set_circuit(adj:Dict[int, List[int]],\n", + " n_nodes: int,\n", + " mixer_angles: List[float],\n", + " cost_angles: List[float]) -> Circuit:\n", + " \n", + " assert len(mixer_angles) == len(cost_angles)\n", + " \n", + " # initial state\n", + " qaoa_circuit = qaoa_initial_circuit(n_nodes)\n", + " \n", + " # add cost and mixer terms to state\n", + " for cost, mixer in zip(cost_angles, mixer_angles):\n", + " cost_ham = qaoa_graph_to_cost_hamiltonian(adj, cost)\n", + " mixer_ham = QubitPauliOperator({QubitPauliString([Qubit(i)], [Pauli.X]): mixer for i in range(n_nodes)})\n", + " qaoa_circuit.append(gen_term_sequence_circuit(cost_ham, Circuit(n_nodes)))\n", + " qaoa_circuit.append(gen_term_sequence_circuit(mixer_ham, Circuit(n_nodes)))\n", + " \n", + " Transform.DecomposeBoxes().apply(qaoa_circuit)\n", + " return qaoa_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "58f78361-305b-41ef-9aa7-08a4b6a17b47", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Tuple\n", + "from pytket.backends.backendresult import BackendResult\n", + "\n", + "def sat_dominating_set(adj:Dict[int, List[int]], meas):\n", + " # how many dominating set constrainst are satisfied \n", + " num = 0\n", + " for node in adj.keys():\n", + " sat = 0\n", + " for x in adj[node]:\n", + " if meas[x] == 1:\n", + " sat = 1\n", + " num+=sat\n", + " return num\n", + "\n", + "def get_energy(adj:Dict[int, List[int]], results: BackendResult) -> float:\n", + " dist = results.get_distribution()\n", + " energy = 0.0\n", + " for meas, prob in dist.items():\n", + " energy += (sat_dominating_set(adj, meas) - len(adj) - 0.1*sum(meas))*prob\n", + "\n", + " return energy" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "a183907e-714e-4a5f-be72-4610a33ea122", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket.backends.backend import Backend\n", + "from typing import Callable\n", + "import numpy as np\n", + "\n", + "def qaoa_instance(\n", + " backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " guess_mixer_angles: np.array,\n", + " guess_cost_angles: np.array,\n", + " seed: int,\n", + " shots: int = 5000,\n", + ") -> float:\n", + " # step 1: get state guess\n", + " my_prep_circuit = qaoa_dominating_set_circuit(\n", + " adj, N, guess_mixer_angles, guess_cost_angles\n", + " )\n", + " measured_circ = my_prep_circuit.copy().measure_all()\n", + " compiler_pass(measured_circ)\n", + " res = backend.run_circuit(measured_circ, shots, seed=seed)\n", + "\n", + " return get_energy(adj, res)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "f562e4e6-2b7f-4d0e-880c-613f5c20eb85", + "metadata": {}, + "outputs": [], + "source": [ + "def qaoa_optimise_energy(compiler_pass: Callable[[Circuit], bool],\n", + " backend: Backend,\n", + " iterations: int = 100,\n", + " n: int = 3,\n", + " shots: int = 5000,\n", + " seed: int= 12345):\n", + " \n", + " highest_energy = -1000 \n", + " best_guess_mixer_angles = [0 for i in range(n)] \n", + " best_guess_cost_angles = [0 for i in range(n)]\n", + " rng = np.random.default_rng(seed)\n", + " # guess some angles (iterations)-times and try if they are better than the best angles found before\n", + " \n", + " for i in range(iterations):\n", + " \n", + " guess_mixer_angles = rng.uniform(0, 1, n)\n", + " guess_cost_angles = rng.uniform(0, 1, n)\n", + " \n", + " qaoa_energy = qaoa_instance(backend,\n", + " compiler_pass,\n", + " guess_mixer_angles,\n", + " guess_cost_angles,\n", + " seed=seed,\n", + " shots=shots)\n", + " \n", + " if(qaoa_energy > highest_energy):\n", + " \n", + " print(\"new highest energy found: \", qaoa_energy)\n", + " \n", + " best_guess_mixer_angles = np.round(guess_mixer_angles, 3)\n", + " best_guess_cost_angles = np.round(guess_cost_angles, 3)\n", + " highest_energy = qaoa_energy\n", + " \n", + " print(\"highest energy: \", highest_energy)\n", + " print(\"best guess mixer angles: \", best_guess_mixer_angles)\n", + " print(\"best guess cost angles: \", best_guess_cost_angles)\n", + " return best_guess_mixer_angles, best_guess_cost_angles" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "2e8941c3-b604-45c6-8aba-de496cca9a80", + "metadata": {}, + "outputs": [], + "source": [ + "def qaoa_calculate(backend: Backend,\n", + " compiler_pass: Callable[[Circuit], bool],\n", + " shots: int = 5000,\n", + " iterations: int = 100,\n", + " seed: int = 12345,\n", + " ) -> BackendResult:\n", + " \n", + " # find the parameters for the highest energy\n", + " best_mixer, best_cost = qaoa_optimise_energy(compiler_pass,\n", + " backend,\n", + " iterations,\n", + " 3,\n", + " shots=shots,\n", + " seed=seed)\n", + " \n", + " # get the circuit with the final parameters of the optimisation:\n", + " my_qaoa_circuit = qaoa_dominating_set_circuit(adj,\n", + " N,\n", + " best_mixer,\n", + " best_cost)\n", + "\n", + " my_qaoa_circuit.measure_all()\n", + "\n", + " compiler_pass(my_qaoa_circuit)\n", + " handle = backend.process_circuit(my_qaoa_circuit, shots, seed=seed)\n", + "\n", + " result = backend.get_result(handle) \n", + " \n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "377cf5cf-21cd-492a-8bab-d4a9443aea53", + "metadata": {}, + "outputs": [], + "source": [ + "from pytket.extensions.qiskit import AerBackend\n", + "\n", + "backend = AerBackend()\n", + "comp = backend.get_compiled_circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "1b7112e5-eb3f-4a40-8165-a3fa7f9ce26a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new highest energy found: -1.1560000000000001\n", + "new highest energy found: -0.955\n", + "new highest energy found: -0.32600000000000007\n", + "highest energy: -0.32600000000000007\n", + "best guess mixer angles: [0.734 0.22 0.082]\n", + "best guess cost angles: [0.16 0.34 0.465]\n", + "CPU times: user 13 s, sys: 0 ns, total: 13 s\n", + "Wall time: 13 s\n" + ] + } + ], + "source": [ + "%%time\n", + "res = qaoa_calculate(backend, backend.default_compilation_pass(2).apply, shots = 100, iterations = 20, seed=12345)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "f002c01a-2224-48e9-b038-cef48dbabadb", + "metadata": {}, + "outputs": [], + "source": [ + "def prob_each_node(dist):\n", + " p = [0]*N\n", + " for meas, prob in dist.items():\n", + " for ind, val in enumerate(meas):\n", + " if (val == 1): \n", + " p[ind] +=prob\n", + " return p" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "373de61a-7196-4437-8ea3-d3a57bffa2f0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.42, 0.8, 0.52, 0.42]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "prob_each_node(res.get_distribution())" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "id": "fd60112a-7bfe-41e6-b0ca-a810331c4150", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.32600000000000007" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_energy(adj, res)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba155c72-d9ba-42c9-8f13-3a7ce445199a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/team_solutions/pineapple/overnight_2.py b/team_solutions/pineapple/overnight_2.py new file mode 100644 index 0000000..fa41409 --- /dev/null +++ b/team_solutions/pineapple/overnight_2.py @@ -0,0 +1,203 @@ +import os +from matplotlib import pyplot as plt +from pytket.extensions.qiskit import AerBackend +from pytket.backends.backend import Backend +from pytket.backends.backendresult import BackendResult +from pytket.passes import DecomposeBoxes +from pytket.utils import gen_term_sequence_circuit +import numpy as np +from pytket import Qubit, Circuit +from pytket.pauli import QubitPauliString, Pauli +from pytket.utils import QubitPauliOperator +from typing import List, Tuple, Callable +import networkx as nx +import networkx.algorithms.isomorphism.vf2userfunc as vf2 +import networkx as nx +import numpy as np +import matplotlib.pyplot as plt +import networkx.algorithms.isomorphism.vf2userfunc as vf2 + +import time + +import pickle + + +def single_edge_energy(results: BackendResult) -> float: + """ + Return the expected enery of a edge 0-1. + """ + dist = results.get_distribution() + return sum((meas[0] ^ meas[1]) * prob for meas, prob in dist.items()) + + +def subgraphInduce(G, edge, depth, rename=True): + # return: subgraph induced by edge + current = set(edge) + edges = set([edge]) + for i in range(depth): + next = set() + for node in current: + next.update(G.neighbors(node)) + edges.update(G.edges(node)) + current.update(next) + if rename: + # Rename nodes to 0, 1, 2, ... such that 0 and 1 are the central edge. + current.remove(edge[0]) + current.remove(edge[1]) + current = [edge[0], edge[1]] + list(current) + + edges = [(current.index(e[0]), current.index(e[1])) + for e in edges] + return nx.Graph(list(edges)) + + +SUBGRAPHS = pickle.load(open("subgraphs.pkl", "rb")) +needed = [22, 20, 13] +SUBGRAPHS = [SUBGRAPHS[i] for i in needed] +# print(len(SUBGRAPHS)) +# for i in SUBGRAPHS: +# print(i) + + +def qaoa_initial_circuit(n_qubits: int) -> Circuit: + c = Circuit(n_qubits) + for i in range(n_qubits): + c.H(i) + return c + + +def qaoa_graph_to_cost_hamiltonian( + edges: List[Tuple[int, int]], cost_angle: float +) -> QubitPauliOperator: + """ + This function takes a list of edges and a cost angle and returns a QubitPauliOperator + representing the cost Hamiltonian for the QAOA algorithm. + + """ + qpo_dict = {QubitPauliString(): len(edges) * 0.5 * cost_angle} + for e in edges: + term_string = QubitPauliString( + [Qubit(e[0]), Qubit(e[1])], [Pauli.Z, Pauli.Z]) + qpo_dict[term_string] = -0.5 * cost_angle + return QubitPauliOperator(qpo_dict) + + +def qaoa_max_cut_circuit( + edges: List[Tuple[int, int]], + mixer_angles: List[float], + cost_angles: List[float], +) -> Circuit: + """ + Create a QAOA circuit for the MaxCut problem. + """ + + n_nodes: int = len(set().union(*edges)) + + # print(len(mixer_angles), len(cost_angles)) + + assert len(mixer_angles) == len(cost_angles) + + # initial state + qaoa_circuit = qaoa_initial_circuit(n_nodes) + + # add cost and mixer terms to state + for cost, mixer in zip(cost_angles, mixer_angles): + cost_ham = qaoa_graph_to_cost_hamiltonian(edges, cost) + mixer_ham = QubitPauliOperator( + {QubitPauliString([Qubit(i)], [Pauli.X]) + : mixer for i in range(n_nodes)} + ) + qaoa_circuit.append(gen_term_sequence_circuit( + cost_ham, Circuit(n_nodes))) + qaoa_circuit.append(gen_term_sequence_circuit( + mixer_ham, Circuit(n_nodes))) + + DecomposeBoxes().apply(qaoa_circuit) + return qaoa_circuit + + +def single_precompute(graph, backend, compiler_pass, shots, discretization, seed): + """ + This function precomputes the results for a small k-regular graphs for many angles. + For instance, if discretization = (10, 10, 10, 10) [param_num = 4] + Then we will return results for mixer_angles = (i/10, j/10) and + cost_angles = (k/10, l/10) for all i, j, k, l in {0, ..., 9} + These are formatted as [*mixer_angles, *cost_angles] + + """ + # print(discretization) + results = np.zeros(discretization) + for index in np.ndindex(*discretization): + true_index = np.array(index) / discretization # normalize to [0, 1] + mixer_angles = true_index[:len(index) // 2] + cost_angles = true_index[len(index) // 2:] + + # get the circuit with the final parameters of the optimisation: + circuit = qaoa_max_cut_circuit( + graph, mixer_angles, cost_angles + ) + + circuit.measure_all() + + compiler_pass(circuit) + handle = backend.process_circuit(circuit, shots, seed=seed) + res = single_edge_energy(backend.get_result(handle)) + if (sum(index[1:]) == 0): + print("Angles", mixer_angles, cost_angles) + # time.sleep(5) # cool down. + # print("Angles", mixer_angles, cost_angles, "Energy:", res) + + results[index] = res + return results + + +def filename(graph): + h = "".join(str(i[0]) + str(i[1]) for i in graph) + return h + + +def precompute(backend, compiler_pass, shots, discretization, seed): + # precompute the results for all graphs + """ + This function precomputes the results for a small k-regular graphs for many angles. + For now we only precompute for a hardcoded list of k = 3, p = 1. + Node 0 = j, Node 1 = k. + """ + # TODO: don't cheat. + + for graph in SUBGRAPHS: + # Check if we have already computed this graph + if os.path.exists(filename(graph) + ".pkl"): + print("Skipping", graph) + continue + + now = time.time() + print(now) + print("subgraph: ", graph) + x = single_precompute( + graph, backend, compiler_pass, shots, discretization, seed + ) + + what = filename(graph) + + pickle.dump(x, open(str(what) + ".pkl", "wb")) + + print("Maximum Energy: ", np.max(x)) + print(time.time() - now) + + +# y = pickle.load(open("7073760102818118101112925114.pkl", "rb")) +# print(y.shape) +# print(y) + +print(len(SUBGRAPHS)) +print(len(set([filename(i) for i in SUBGRAPHS]))) +backend = AerBackend() +comp = backend.get_compiled_circuit + + +DISCRETIZATION = (10, 10, 10, 10) + +PRECOMPUTE = precompute(backend, + backend.default_compilation_pass(0).apply, + 5000, DISCRETIZATION, 12345) diff --git a/team_solutions/pineapple/overnight_compile.py b/team_solutions/pineapple/overnight_compile.py new file mode 100644 index 0000000..0bf5085 --- /dev/null +++ b/team_solutions/pineapple/overnight_compile.py @@ -0,0 +1,74 @@ +import os +from matplotlib import pyplot as plt +from pytket.extensions.qiskit import AerBackend +from pytket.backends.backend import Backend +from pytket.backends.backendresult import BackendResult +from pytket.passes import DecomposeBoxes +from pytket.utils import gen_term_sequence_circuit +import numpy as np +from pytket import Qubit, Circuit +from pytket.pauli import QubitPauliString, Pauli +from pytket.utils import QubitPauliOperator +from typing import List, Tuple, Callable +import networkx as nx +import networkx.algorithms.isomorphism.vf2userfunc as vf2 +import networkx as nx +import numpy as np +import matplotlib.pyplot as plt +import networkx.algorithms.isomorphism.vf2userfunc as vf2 + +import pickle + + +SUBGRAPHS = pickle.load(open("subgraphs.pkl", "rb")) + +print(len(SUBGRAPHS)) +for i in SUBGRAPHS: + print(i) + + +def filename(graph): + h = "".join(str(i[0]) + str(i[1]) for i in graph) + return h + +# Collect all subgraphs into one file + + +results = [] + +for i, graph in enumerate(SUBGRAPHS): + # Check if we have already computed this graph + if os.path.exists(str(filename(graph)) + ".pkl"): + results.append(pickle.load(open(str(filename(graph)) + ".pkl", "rb"))) + print(i, "#", len(results)) + x = results[-1] + # print(x.shape) + # rft = np.fft.rfftn(x) + # rft[4:, :, :, :] = 0 + # rft[:, 4:, :, :] = 0 + # rft[:, :, 4:, :] = 0 + # rft[:, :, :, 4:] = 0 + + # x_smooth = np.fft.irfftn(rft) + # # Sum along last two axes ( we are left with mixer ) + # visualize_x = np.sum(x, axis=(2, 3)) + # visualize_x_smooth = np.sum(x_smooth, axis=(2, 3)) + + # # Sum along the first two axes ( we are left with cost ) + # visualize_y = np.sum(x, axis=(0, 1)) + # visualize_y_smooth = np.sum(x_smooth, axis=(0, 1)) + + # f, axarr = plt.subplots(2, 2) + # axarr[0][0].imshow(visualize_x, cmap="hot", interpolation="nearest") + # axarr[0][1].imshow(visualize_x_smooth, cmap="hot", + # interpolation="nearest") + # axarr[1][0].imshow(visualize_y, cmap="hot", interpolation="nearest") + # axarr[1][1].imshow(visualize_y_smooth, cmap="hot", + # interpolation="nearest") + + # plt.show() + else: + results.append(None) + print(i, "X") + +pickle.dump(results, open("p2data.pkl", "wb")) diff --git a/team_solutions/pineapple/overnight_computation.py b/team_solutions/pineapple/overnight_computation.py new file mode 100644 index 0000000..3c3f90b --- /dev/null +++ b/team_solutions/pineapple/overnight_computation.py @@ -0,0 +1,101 @@ +from matplotlib import pyplot as plt +from pytket.extensions.qiskit import AerBackend +from pytket.backends.backend import Backend +from pytket.backends.backendresult import BackendResult +from pytket.passes import DecomposeBoxes +from pytket.utils import gen_term_sequence_circuit +import numpy as np +from pytket import Qubit, Circuit +from pytket.pauli import QubitPauliString, Pauli +from pytket.utils import QubitPauliOperator +from typing import List, Tuple, Callable +import networkx as nx +import networkx.algorithms.isomorphism.vf2userfunc as vf2 +import networkx as nx +import numpy as np +import matplotlib.pyplot as plt +import networkx.algorithms.isomorphism.vf2userfunc as vf2 + +import pickle + + +def subgraphInduce(G, edge, depth, rename=True): + # return: subgraph induced by edge + current = set(edge) + edges = set([edge]) + for i in range(depth): + next = set() + for node in current: + next.update(G.neighbors(node)) + edges.update(G.edges(node)) + current.update(next) + if rename: + # Rename nodes to 0, 1, 2, ... such that 0 and 1 are the central edge. + current.remove(edge[0]) + current.remove(edge[1]) + current = [edge[0], edge[1]] + list(current) + + edges = [(current.index(e[0]), current.index(e[1])) + for e in edges] + return nx.Graph(list(edges)) + + +def gimme_subgraphs(): + # load subgraphs from file + found = True + + try: + x = pickle.load(open("subgraphs3.pkl", "rb")) + except: + found = False + if found: + found = input( + "Input y to use existing subgraphs, else will generate new ones: ") + if found: + return x + else: + res = x # Continue from savepoint + + for j in range(100): + for t in range(20, 40, 2): + G = nx.random_regular_graph(3, t) + for edge in G.edges(): + subgraph = subgraphInduce(G, edge, 2) + seen = False + for j in range(len(res)): + if nx.is_isomorphic(subgraph, res[j][0]): + seen = True + res[j][1] += 1 + break + if not seen: + # print(len(res)) + res.append([subgraph, 1]) + # res.append(subgraph) + res = sorted(res, key=lambda x: x[1]) + + print("Generated Subgraphs len = ", len(res), "Sorted by size = ", " ".join( + map(str, sorted([(i[1], len(i[0].edges)) for i in res])[:-100:-1]))) + for j in range(1000): + for t in range(20, 100, 2): + G = nx.random_regular_graph(3, t) + for edge in G.edges(): + subgraph = subgraphInduce(G, edge, 2) + seen = False + for j in range(len(res)): + if nx.is_isomorphic(subgraph, res[j][0]): + seen = True + res[j][1] += 1 + break + if not seen: + # print(len(res)) + res.append([subgraph, 1]) + # res.append(subgraph) + # sort by size + res = sorted(res, key=lambda x: x[1]) + print("Generated Subgraphs len = ", len(res), "Sorted by size = ", " ".join( + map(str, sorted([(i[1], len(i[0].edges)) for i in res])[:-100:-1]))) + + pickle.dump(res, open("subgraphs3.pkl", "wb")) + + +gimme_subgraphs() diff --git a/team_solutions/pineapple/team_solutions.md b/team_solutions/pineapple/team_solutions.md new file mode 100644 index 0000000..945188c --- /dev/null +++ b/team_solutions/pineapple/team_solutions.md @@ -0,0 +1,16 @@ +# Team Submission + +[Documentation](https://www.overleaf.com/read/tzjzwhxkhnzx) + +Code: +1. Optimization +* [SGD](maxcutSGD.py) +2. Mapping Other Problems +* [Minimum Dominating Set](minimum_dominating_set.ipynb) +* [Four Coloring](four_color.ipynb) +3. Reducing Compilation Time +* [Symbolic Compiler](maxcut_symbolic_compiler.ipynb) +* [Binary Lifting](maxcut_notebook-optimizecompilation-binarylifting.ipynb) +* [Precomputation](maxcut_notebook_precompute_all.ipynb) +4. Other open-ended investigation +* [Interpolation](maxcut_interpolate.py) diff --git a/team_solutions/team_solutions.md b/team_solutions/team_solutions.md deleted file mode 100644 index 71eb0e7..0000000 --- a/team_solutions/team_solutions.md +++ /dev/null @@ -1,6 +0,0 @@ -# List of Projects - -### Sample Team - -- [Code](TEAM_NAME) -- [Documentation](https://url_to_documentation.com) \ No newline at end of file