From 8e06818342d59a295a0875746f564e7490fe13c7 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:28:07 -0400 Subject: [PATCH 01/38] added proper doc string to ising_trans() --- grove/ising/ising_qaoa.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 3f6ba28..da2aa5a 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -38,7 +38,13 @@ def print_fun(x): def ising_trans(x): - # Transformation to Ising notation + """ + Transformation to Ising notation. + + :param x: (int) Value of a single binary bit from {0, 1}. + :return: Transformed bit value from {-1, 1}. + :rtype: Integer. + """ if x == 1: return -1 else: @@ -119,8 +125,7 @@ def ising(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samp vqe_options=vqe_option) betas, gammas = qaoa_inst.get_angles() - most_freq_string, sampling_results = qaoa_inst.get_string( - betas, gammas) + most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas) most_freq_string_ising = [ising_trans(it) for it in most_freq_string] energy_ising = energy_value(h, J, most_freq_string_ising) param_prog = qaoa_inst.get_parameterized_program() From 73580dbe2d3322fb244c0c0849ee08145b92bf20 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:41:23 -0400 Subject: [PATCH 02/38] changed ``h`` argument from list to dict Ising instances can have many variables but almost no bias terms so a dictionary is naturally a better choice in order to account for sparse bias vectors --- grove/ising/ising_qaoa.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index da2aa5a..7d53a36 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -55,10 +55,10 @@ def ising(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samp initial_beta=None, initial_gamma=None, minimizer_kwargs=None, vqe_option=None): """ - Ising set up method + Ising set up method for QAOA. Supports 2-local as well as k-local interaction terms. - :param h: External magnectic term of the Ising problem. List. - :param J: Interaction term of the Ising problem. Dictionary. + :param h: (dict) External magnectic term of the Ising problem. + :param J: (dict) Interaction terms of the Ising problem (may be k-local). :param num_steps: (Optional.Default=2 * len(h)) Trotterization order for the QAOA algorithm. :param verbose: (Optional.Default=True) Verbosity of the code. @@ -95,7 +95,7 @@ def ising(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samp for i, j in J.keys(): cost_operators.append(PauliSum([PauliTerm("Z", i, J[(i, j)]) * PauliTerm("Z", j)])) - for i in range(n_nodes): + for i in h.keys(): cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])])) for i in range(n_nodes): From fa65ef3642bd63cd5d7595428195b63fbd516588 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:44:18 -0400 Subject: [PATCH 03/38] extracting qubit indices from ``h`` and ``J`` Ising instances can make use of arbitrary qubits especially when we have to map to a QPU hardware graph. Makes much more sense to extract the qubit indices and compute n_nodes like this. --- grove/ising/ising_qaoa.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 7d53a36..89fd073 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -88,7 +88,9 @@ def ising(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samp if num_steps == 0: num_steps = 2 * len(h) - n_nodes = len(h) + qubit_indices = set([ index for tuple_ in list(J.keys()) for index in tuple_] + + list(h.keys())) + n_nodes = len(qubit_indices) cost_operators = [] driver_operators = [] @@ -98,7 +100,7 @@ def ising(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samp for i in h.keys(): cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])])) - for i in range(n_nodes): + for i in qubit_indices: driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)])) if connection is None: From 6d4e2301ff45157feda283ef5051887365342893 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:46:06 -0400 Subject: [PATCH 04/38] renamed ising() to ising_qaoa() to make clear that it is a QAOA wrapper --- grove/ising/ising_qaoa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 89fd073..f52be82 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -51,7 +51,7 @@ def ising_trans(x): return 1 -def ising(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samples=None, +def ising_qaoa(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samples=None, initial_beta=None, initial_gamma=None, minimizer_kwargs=None, vqe_option=None): """ From 9cd02554639ce5bb570a0fc4f91ea493ee5be329 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:49:54 -0400 Subject: [PATCH 05/38] cleaned up doc string for better read the docs later on --- grove/ising/ising_qaoa.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index f52be82..8fee7b9 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -67,21 +67,19 @@ def ising_qaoa(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, :param connection: (Optional) connection to the QVM. Default is None. :param samples: (Optional. Default=None) VQE option. Number of samples (circuit preparation and measurement) to use in operator - averaging. + averaging. Required when using QPU backend. :param initial_beta: (Optional. Default=None) Initial guess for beta parameters. :param initial_gamma: (Optional. Default=None) Initial guess for gamma parameters. :param minimizer_kwargs: (Optional. Default=None). Minimizer optional arguments. If None set to - {'method': 'Nelder-Mead', - 'options': {'ftol': 1.0e-2, 'xtol': 1.0e-2, - 'disp': False} - :param vqe_option: (Optional. Default=None). VQE optional - arguments. If None set to + {'method': 'Nelder-Mead', 'options': {'ftol': 1.0e-2, + 'xtol': 1.0e-2, disp': False} + :param vqe_option: (Optional. Default=None). VQE optional arguments. If None set to vqe_option = {'disp': print_fun, 'return_all': True, 'samples': samples} - :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result. + :return: Most frequent Ising string, energy of the Ising string, circuit used to obtain result. :rtype: List, Integer or float, 'pyquil.quil.Program'. """ From eb227400e88c3139b999db59854ccc97fa0261f9 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:50:15 -0400 Subject: [PATCH 06/38] supporting k-local interaction terms in J --- grove/ising/ising_qaoa.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 8fee7b9..7eb8c8c 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -92,8 +92,16 @@ def ising_qaoa(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, cost_operators = [] driver_operators = [] - for i, j in J.keys(): - cost_operators.append(PauliSum([PauliTerm("Z", i, J[(i, j)]) * PauliTerm("Z", j)])) + for key in J.keys(): + # first PauliTerm is multiplied with coefficient obtained from J + pauli_product = PauliTerm("Z", key[0], J[key]) + + for i in range(1,len(key)): + # multiply with additional Z PauliTerms depending + # on the locality of the interaction terms + pauli_product *= PauliTerm("Z", key[i]) + + cost_operators.append(PauliSum([pauli_product])) for i in h.keys(): cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])])) From 95a58d39047c1649df55bc0afcad6c8305fceeaa Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:52:27 -0400 Subject: [PATCH 07/38] energy_value() now supports k-local interaction terms --- grove/ising/ising_qaoa.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 7eb8c8c..848d673 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -15,20 +15,25 @@ def energy_value(h, J, sol): """ Obtain energy of an Ising solution for a given Ising problem (h,J). - :param h: External magnectic term of the Ising problem. List. - :param J: Interaction term of the Ising problem. Dictionary. - :param sol: Ising solution. List. + :param h: (dict) External magnetic term of the Ising problem. + :param J: (dict) Interaction terms of the Ising problem (may be k-local). + :param sol: (list) Ising solution. :return: Energy of the Ising string. :rtype: Integer or float. """ ener_ising = 0 for elm in J.keys(): - if elm[0] == elm[1]: - raise TypeError("""Interaction term must connect two different variables""") + paired_indices = [(a, b) for a, b in zip(elm, elm)] + if len(paired_indices) != len(set(paired_indices)): + raise TypeError(f"Interaction term must connect different variables. The term {elm} contains a duplicate.") else: - ener_ising += J[elm] * int(sol[elm[0]]) * int(sol[elm[1]]) - for i in range(len(h)): + multipliers = int(sol[elm[0]]) * int(sol[elm[1]]) + # if locality > 2 then add more multipliers + for i in range(2, len(elm)): + multipliers *= sol[elm[i]] + ener_ising += J[elm] * multipliers + for i in h.keys(): ener_ising += h[i] * int(sol[i]) return ener_ising From 23221f10f00a2448b884c00a7e6d708be72d4c34 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:58:19 -0400 Subject: [PATCH 08/38] fixed existing tests for ising_qaoa() --- grove/tests/ising/test_ising.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index b76d8d5..cff8a62 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -1,4 +1,4 @@ -from grove.ising.ising_qaoa import ising +from grove.ising.ising_qaoa import ising_qaoa from grove.ising.ising_qaoa import energy_value import numpy as np from mock import patch @@ -6,7 +6,7 @@ def test_energy_value(): J = {(0, 1): 2.3} - h = [-2.4, 5.2] + h = {0: -2.4, 1: 5.2} sol = [1, -1] ener_ising = energy_value(h, J, sol) @@ -20,9 +20,9 @@ def test_ising_mock(): cxn.expectation.return_value = [-0.4893891813015294, 0.8876822987380573, -0.4893891813015292, -0.9333372094534063, -0.9859245403423198, 0.9333372094534065] J = {(0, 1): -2, (2, 3): 3} - h = [1, 1, -1, 1] + h = {0: 1, 1: 1, 2: -1, 3: 1} p = 1 - most_freq_string_ising, energy_ising, circuit = ising(h, J, num_steps=p, vqe_option=None, connection=cxn) + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, num_steps=p, vqe_option=None, connection=cxn) assert most_freq_string_ising == [-1, -1, 1, -1] assert energy_ising == -9 From dcdcf7da29592d46d3c928391e0c0ef0f55b0410 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:00:45 -0400 Subject: [PATCH 09/38] more detailed tests for ising_qaoa() --- grove/tests/ising/test_ising.py | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index cff8a62..6a82fb2 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -26,3 +26,57 @@ def test_ising_mock(): assert most_freq_string_ising == [-1, -1, 1, -1] assert energy_ising == -9 + + with patch("pyquil.api.QVMConnection") as cxn: + # Mock the response + cxn.run_and_measure.return_value = [[1, 0, 1, 0]] + cxn.expectation.return_value = [0, 0, 0, 0] # dummy + + # checkerboard with couplings + J = {(0, 1): 1, (0, 2): 1, (1, 3): 1, (2, 3): 1} + h = {} + p = 1 + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, num_steps=p, vqe_option=None, connection=cxn) + + assert most_freq_string_ising == [-1, 1, -1, 1] + assert energy_ising == 0 + + with patch("pyquil.api.QVMConnection") as cxn: + # Mock the response + cxn.run_and_measure.return_value = [[1, 0, 1, 0]] + cxn.expectation.return_value = [0, 0, 0, 0] # dummy + + # checkerboard with biases + J = {} + h = {0: 1, 1: -1, 2: 1, 3: -1} + p = 1 + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, num_steps=p, vqe_option=None, connection=cxn) + + assert most_freq_string_ising == [-1, 1, -1, 1] + assert energy_ising == -4 + + with patch("pyquil.api.QVMConnection") as cxn: + # Mock the response + cxn.run_and_measure.return_value = [[1, 0, 1, 0, 1]] + cxn.expectation.return_value = [0, 0, 0, 0, 0] # dummy + + J = {(0, 4): -1} + h = {0: 1, 1: -1, 2: 1, 3: -1} + p = 1 + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, num_steps=p, vqe_option=None, connection=cxn) + + assert most_freq_string_ising == [-1, 1, -1, 1, -1] + assert energy_ising == -5 + + with patch("pyquil.api.QVMConnection") as cxn: + # Mock the response + cxn.run_and_measure.return_value = [[0, 1, 1, 0]] + cxn.expectation.return_value = [0, 0, 0, 0, 0, 0, 0] # dummy + + J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5 , (0, 2, 3): 0.5, (1, 3): 3.1} + h = {0: -2.4, 1: 5.2 , 3: -0.3} + p = 1 + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, num_steps=p, vqe_option=None, connection=cxn) + + assert most_freq_string_ising == [1, -1, -1, 1] + assert energy_ising == -7.8 From 6d1661be97970f4fd0827bd8040e865f1578e699 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:02:32 -0400 Subject: [PATCH 10/38] new tests added for ising_trans() and energy_value() --- grove/tests/ising/test_ising.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index 6a82fb2..083fae5 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -1,5 +1,4 @@ -from grove.ising.ising_qaoa import ising_qaoa -from grove.ising.ising_qaoa import energy_value +from grove.ising.ising_qaoa import * import numpy as np from mock import patch @@ -12,6 +11,17 @@ def test_energy_value(): assert(np.isclose(ener_ising, -9.9)) + J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5 , (0, 2, 3): 0.5, (1, 3): 3.1} + h = {0: -2.4, 1: 5.2 , 3: -0.3} + sol = [1, -1, -1, 1] + ener_ising = energy_value(h, J, sol) + + assert(np.isclose(ener_ising, -7.8)) + +def test_ising_trans(): + sol = [0, 1, 1, 0] + ising_sol = [ising_trans(bit) for bit in sol] + assert ising_sol == [1, -1, -1, 1] def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: From a63dbe2d31bbff90ae88caa8b5d9dc8bfebe66ca Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:08:00 -0400 Subject: [PATCH 11/38] ``driver_operators`` added to arguments allows for user-defined mixer/driver hamiltonian in order to enforce hard constraints tests & docs also updated --- grove/ising/ising_qaoa.py | 18 +++++++++++++----- grove/tests/ising/test_ising.py | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 848d673..a61e10f 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -56,9 +56,10 @@ def ising_trans(x): return 1 -def ising_qaoa(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, samples=None, - initial_beta=None, initial_gamma=None, minimizer_kwargs=None, - vqe_option=None): +def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, + rand_seed=None, connection=None, samples=None, + initial_beta=None, initial_gamma=None, minimizer_kwargs=None, + vqe_option=None): """ Ising set up method for QAOA. Supports 2-local as well as k-local interaction terms. @@ -66,6 +67,10 @@ def ising_qaoa(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, :param J: (dict) Interaction terms of the Ising problem (may be k-local). :param num_steps: (Optional.Default=2 * len(h)) Trotterization order for the QAOA algorithm. + :param driver_operators: (Optional. Default: X on all qubits.) The mixer/driver + Hamiltonian used in QAOA. Can be used to enforce hard constraints + and ensure that solution stays in feasible subspace. + Must be PauliSum objects. :param verbose: (Optional.Default=True) Verbosity of the code. :param rand_seed: (Optional. Default=None) random seed when beta and gamma angles are not provided. @@ -111,8 +116,11 @@ def ising_qaoa(h, J, num_steps=0, verbose=True, rand_seed=None, connection=None, for i in h.keys(): cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])])) - for i in qubit_indices: - driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)])) + if driver_operators is None: + driver_operators = [] + # default to X mixer + for i in qubit_indices: + driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)])) if connection is None: connection = CXN diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index 083fae5..e75ccbc 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -90,3 +90,23 @@ def test_ising_mock(): assert most_freq_string_ising == [1, -1, -1, 1] assert energy_ising == -7.8 + + with patch("pyquil.api.QVMConnection") as cxn: + # Mock the response + cxn.run_and_measure.return_value = [[0, 1, 1, 0]] + cxn.expectation.return_value = [0, 0, 0, 0, 0, 0, 0] # dummy + + swap_mixer = [] + for i in range(4): + for j in range(4): + if j != i: + swap_mixer.append(PauliSum([PauliTerm("X", i, 0.5) * PauliTerm("X", j, 1.0)])) + swap_mixer.append(PauliSum([PauliTerm("Y", i, 0.5) * PauliTerm("Y", j, 1.0)])) + + J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5 , (0, 2, 3): 0.5, (1, 3): 3.1} + h = {0: -2.4, 1: 5.2 , 3: -0.3} + p = 1 + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, driver_operators=swap_mixer, num_steps=p, vqe_option=None, connection=cxn) + + assert most_freq_string_ising == [1, -1, -1, 1] + assert energy_ising == -7.8 From 3ec2160a0189b9ac85479d7907df2f7874dbde1b Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:15:23 -0400 Subject: [PATCH 12/38] read the docs for Ising QAOA added extensive documentation for the Ising QAOA wrapper added images added ising_qaoa to index.rst --- docs/index.rst | 1 + docs/ising_qaoa.rst | 268 ++++++++++++++++++ docs/ising_qaoa/2d_checkerboard.png | Bin 0 -> 19577 bytes docs/ising_qaoa/2d_checkerboard_solutions.png | Bin 0 -> 16964 bytes docs/ising_qaoa/generalplan.png | Bin 0 -> 29756 bytes docs/ising_qaoa/simple_coupling.png | Bin 0 -> 7717 bytes docs/ising_qaoa/triangle.png | Bin 0 -> 12883 bytes docs/ising_qaoa/triangle_desired.png | Bin 0 -> 5774 bytes grove/ising/ising_qaoa.py | 3 +- 9 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 docs/ising_qaoa.rst create mode 100644 docs/ising_qaoa/2d_checkerboard.png create mode 100644 docs/ising_qaoa/2d_checkerboard_solutions.png create mode 100644 docs/ising_qaoa/generalplan.png create mode 100644 docs/ising_qaoa/simple_coupling.png create mode 100644 docs/ising_qaoa/triangle.png create mode 100644 docs/ising_qaoa/triangle_desired.png diff --git a/docs/index.rst b/docs/index.rst index 3aa5e38..c85fe46 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,6 +23,7 @@ of which has its own self-contained documentation. installation vqe qaoa + ising_qaoa qft phaseestimation tomography diff --git a/docs/ising_qaoa.rst b/docs/ising_qaoa.rst new file mode 100644 index 0000000..ccb451d --- /dev/null +++ b/docs/ising_qaoa.rst @@ -0,0 +1,268 @@ +Ising Wrapper for QAOA +====================== + +Overview +-------- + +Ising QAOA is a wrapper for the +`quantum approximate optimization algorithm `_ +that makes it easy to work within the framework of Ising-type Hamiltonians +with binary variables \\( \\sigma_{k} \\in \\{+1, -1\\} \\). + +This wrapper is particulary useful for people that have been working with quantum annealers +in the past. However, in comparison to current quantum annealers the Ising QAOA wrapper +supports not only 2-local but also k-local interaction terms and the driver (mixer) +Hamiltonian is not dictated by the hardware but can be defined by the user. + +For each Ising instance the user specifies the bias terms \\( h_{i} \\), the +interaction tensor \\( J_{i,j,..,k} \\) and the approximation order of the algorithm. +``ising_qaoa.py`` contains routines for approximating the ground state of +the specified Ising-type Hamiltonian through the use of QAOA which itself +finds the optimal rotation angles via Grove's +`variational-quantum-eigensolver method `_. + +.. _quickstart-example: + +Quickstart Example +------------------- + +To test your installation and get going we can run Ising QAOA for +a very simple Ising Hamiltonian: the 2D checkerboard. +(for a detailed explanation see :ref:`2d-checkerboard`) +In your python script import the packages and connect to your QVM: + +.. code-block:: python + + import pyquil.api as api + from grove.ising.ising_qaoa import ising as ising_qaoa + qvm_connection = api.QVMConnection() + +Next we define the appropriate bias and interaction terms of the Ising Hamiltonian +for the checkerboard problem: + +.. code-block:: python + + J = {(0, 1): 1, (0, 2): 1, (1, 3): 1, (2, 3): 1} + h = {0: -1, 1: 1, 2: 1, 3: -1} + +There are plenty of optional configuration parameters for the algorithm but the two +most important are the number of steps to use for the trotterization (roughly corresponds to +the accuracy of the optimization) and the driver Hamiltonian (which determines how the +state space is searched). We instantiate the algorithm and run the optimization routine on our QVM: + +.. code-block:: python + + steps = 2 + solution_string, ising_energy, _ = ising_qaoa(h=h, J=J, num_steps=steps) + print(f'The algorithm returned {solution_string} with an energy of {ising_energy}') + +When running this routine you should observe the expectation value converging towards -8.0 +and the solution with the highest probability should be \\( [1, -1, -1, 1] \\) with an energy of \\( -8.0 \\). + +We can verify this by running the Ising QAOA multiple times and collecting statistics. +To do this, replace the last line of the last code block with: + +.. code-block:: python + + runs = 10 + stats = dict() + for _ in range(runs): + solution_string, ising_energy, _ = ising_qaoa(h=h, J=J, num_steps=steps) + if tuple(solution_string) in stats.keys(): + stats[tuple(solution_string)] += 1 + else: + stats[tuple(solution_string)] = 1 + print(f'Solution statistics: {stats}') + +You should see that the algorithm returns the aforementioned solution with ~99% probability (unless the classical minimizer got stuck in a local minima). + + +Algorithm and Details +--------------------- + +Introduction +~~~~~~~~~~~~ + +The Ising model is a very famous mathematical model by physicist Ernst Ising that was originally developed +to model ferromagnetism in statistical mechanics. The Ising model can be written as a +quadratic function of a set of spins \\( \\sigma_{i} = \\pm\, 1 \\): + +$$\\mathbf{H}(\\sigma_{i},...,\\sigma_{N}) = - \\sum_{i`_]: + +$$\\mathbf{H}(\\sigma_{i},...,\\sigma_{N}) = - \\sum_{i`_] +we know that finding the ground state of a two-dimensional Ising model without magnetic field lies +in the complexity class \\( P \\). However, as soon as you turn on that external magnetic field +you find yourself in the complexity class \\( NP \\) when trying to find the ground state and that's why +we are in desperate need for quantum computers to help us explore the energy landscape more efficiently! But +keep in mind that there is no proof that quantum computers can solve these types of problems in polynomial time... + +One of the reasons for the Ising model's popularity is the fact that many NP problems, such as number and graph partitioning or 3SAT, can be mapped to +it as discussed in e.g. [`3 `_]. + +This Ising QAOA wrapper automatically generates the cost Hamiltonian for the QAOA based on the provided +biases \\( h_{i} \\) and interaction terms \\( J_{i,j} \\). It operates with binary variables from the +set \\( \\{-1, +1\\} \\) and calculates the energy of the solution string. +Using either the QVM or the QPU, it can be used as a stand alone optimizer or a plugin +optimization routine in a larger environment. The usage pipeline is as follows: +1) formulate your problem in terms of an Ising model, +2) instantiate Ising QAOA with \\( h \\) and \\( J \\), +3) retrieve ground state solution by sampling. + +.. figure:: ising_qaoa/generalplan.png + :align: center + :figwidth: 100% + +The following sections give three concrete examples of how to use the +Ising QAOA wrapper to approximate ground states of various Hamiltonians. + +.. _2d-checkerboard: + +2-local Checkerboard Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before we try to create a checkerboard pattern, let's quickly think about the interaction or coupling term. +Consider two spins \\( \\sigma_{i} \\) and \\( \\sigma_{j} \\) and their 2-local interaction term \\( J_{i,j} \\): + +.. image:: ising_qaoa/simple_coupling.png + :align: center + :scale: 75% + +Suppose there are no biases on spins \\( i \\) and \\( j \\) and the coupling is \\( J_{i,j} = -1 \\). +Which values of \\( \\sigma_{i} \\) and \\( \\sigma_{j} \\) minimize the energy? Both spins should have the same +value, either +1 or -1, in order to get an overall energy of -1. Hence, a negative coupling term *correlates* spins! + +If the coupling is \\( J_{i,j} = +1 \\) the energy is minimized when the two spins have opposite values. +Thus, a positive coupling *anti-correlates* spins! + +Now consider the following two-dimensional graph with spins \\( \\sigma_{0}, \\sigma_{1}, \\sigma_{2}, \\sigma_{3}\\): + +.. image:: ising_qaoa/2d_checkerboard.png + :align: center + :scale: 75% + +Let's define that we colour vertex \\( i \\) black if \\( \\sigma_{i} = -1 \\) and white if \\( \\sigma_{i} = +1 \\). +The goal is to create a checkerboard pattern with the four vertices in the graph. There is various ways of defining +\\( h \\) and \\( J \\) to achieve this result. There are two possible solutions to this problem: + +.. image:: ising_qaoa/2d_checkerboard_solutions.png + :align: center + :scale: 75% + +The example used in the :ref:`quickstart-example` is one way of doing it. However, it involved bias terms which +strongly biased for solution nr. 2. This time we want don't care which solution we get as long as it is a valid +solution. Hence, we don't use any bias terms and only anticorrelate each pair of neighbouring spins: + +.. code-block:: python + + import pyquil.api as api + from grove.ising.ising_qaoa import ising as ising_qaoa + qvm_connection = api.QVMConnection() + + J = {(0, 1): 1, (0, 2): 1, (1, 3): 1, (2, 3): 1} + h = {} + + +Given this Ising problem, we run the QAOA algorithm with two \\( \\beta \\) and two \\( \\gamma \\) parameters (``steps=2``). +We run it ten times in order to collect some statistics: + +.. code-block:: python + + steps = 2 + runs = 10 + stats = dict() + for _ in range(runs): + solution_string, ising_energy, _ = ising_qaoa(h=h, J=J, num_steps=steps) + if tuple(solution_string) in stats.keys(): + stats[tuple(solution_string)] += 1 + else: + stats[tuple(solution_string)] = 1 + print(f'Solution statistics: {stats}') + +You should get the two possible solution strings \\( [-1, 1, 1, -1] \\) and \\( [1, -1, -1, 1] \\) +with roughly equal probabilities and an energy value of \\( -4.0 \\) each. + +3-local Triangle Example +~~~~~~~~~~~~~~~~~~~~~~~~ + +To illustrate the use of Ising QAOA with k-local interaction terms we will now consider a triangular graph. +This time we are dealing with 3 spins \\( \\sigma_{0}, \\sigma_{1}, \\sigma_{2}\\): + +.. image:: ising_qaoa/triangle.png + :align: center + :scale: 75% + +where the orange plane represents the 3-local interactions \\( J_{0,1,2} \\). The goal is to colour the graph +such that it looks like this: + +.. image:: ising_qaoa/triangle_desired.png + :align: center + :scale: 75% +Let's again define that we colour vertex \\( i \\) black if \\( \\sigma_{i} = -1 \\) and white if \\( \\sigma_{i} = +1 \\). +To get the desired colouring we define a strongly negative 3-local interaction term \\( J_{0,1,2} \\). This ensures that +either all vertices are coloured white or two vertices are coloured black. In order to incentivize the latter, +we set the following biases on \\( \\sigma_{0} \\) and \\( \\sigma_{1}\\): + +.. code-block:: python + + import pyquil.api as api + from grove.ising.ising_qaoa import ising as ising_qaoa + + qvm_connection = api.QVMConnection() + + J = {(0,1,2): -3} + h = {0: 1, 1: -1} + +We can now run the algorithm ten times with step size 2 to collect statistics (this might take a couple of minutes): + +.. code-block:: python + + steps = 2 + runs = 10 + stats = dict() + for _ in range(runs): + solution_string, ising_energy, _ = ising_qaoa(h=h, J=J, num_steps=steps) + if tuple(solution_string) in stats.keys(): + stats[tuple(solution_string)] += 1 + else: + stats[tuple(solution_string)] = 1 + print(f'Solution statistics: {stats}') + +The majority of the results should be the correct solution string \\( [-1, 1, -1 ] \\) (with energy -5.0). + +Source Code Docs +---------------- + +Here you can find documentation for the different functions of Ising QAOA. + +grove.ising.ising_qaoa.ising_qaoa +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: grove.ising.ising_qaoa.ising_qaoa + +grove.ising.ising_qaoa.ising_trans +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: grove.ising.ising_qaoa.ising_trans + +grove.ising.ising_qaoa.energy_value +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: grove.ising.ising_qaoa.energy_value diff --git a/docs/ising_qaoa/2d_checkerboard.png b/docs/ising_qaoa/2d_checkerboard.png new file mode 100644 index 0000000000000000000000000000000000000000..04eb90e8631e5fc22c0d44d1a885a9e89fe31f05 GIT binary patch literal 19577 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 Awg3PC literal 0 HcmV?d00001 diff --git a/docs/ising_qaoa/2d_checkerboard_solutions.png b/docs/ising_qaoa/2d_checkerboard_solutions.png new file mode 100644 index 0000000000000000000000000000000000000000..83bde03fcf1365b862740037d619a70510093e5e GIT binary patch literal 16964 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 literal 0 HcmV?d00001 diff --git a/docs/ising_qaoa/generalplan.png b/docs/ising_qaoa/generalplan.png new file mode 100644 index 0000000000000000000000000000000000000000..7b6a17c1613bb8a6a4f34e36214a9736d4190b29 GIT binary patch literal 29756 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 literal 0 HcmV?d00001 diff --git a/docs/ising_qaoa/simple_coupling.png b/docs/ising_qaoa/simple_coupling.png new file mode 100644 index 0000000000000000000000000000000000000000..412875cb853581ba4da25e97dd0f6db864c2cf20 GIT binary patch literal 7717 zcmW+*2RxMjAAjtTaUqVZvx$ssiY{k{kj$(jd+(8X_TiU=I24JjY?%oe$wkP>mX#eD zS^uy9z1$u5I?wZczMs$My`K07I%?FEER+xgQERBH=!5ru@Kh%!1+T`l1Ss%E?2XiT zNDdw$AHLpytqMg9el{_gEICpaDU?CZ|!9V`TP6dbaZp}Mq7K> z-E{Y|&)!mGfgrYg4VAkO18|!;fgul3^*v#Rz6vL*jP&g=auaeG<1;%9g>~dAJzTO{ zR3g9qD`(st&pQ(T(%PwBB_|}v&&l?_6;Q!24?z@K<6M-4+75JdzSKV;pM>5sHsuv>f~J9+>b?`C?pNeFu4=fOtd_Q z*^Ci{dU}d8ikM>Z^SLn5(er1Avueu9;kOeV7`ih@?zFqhZK4^O9+;bB*T>!yLHYUl zxZeVswd_Xzol{Vzxp365Ug_w6cFY*q{`%Or(S=$m^6)Ka0osQKsg)s!1ImR;2;h(d zfjQwTg8!)J6rtGq^khHAYvG@u7$}Yxvu}$U26STCCs){cf!;b>Yfd{jAy&+1^r$-jRI?d{47 zYioU2Rql_Ti!sH;#mbJ3j!9<{-9EUzR_K6moaWWw3{t+sB!u8TCkFiR2qY|!K%YK+ zYO(*8TN`}1)ipcIF*7r>^Y{eS#bdY^7;Aso9VrFFzABFim`D2Fd@GfdGFqFv0lOW&V1)kv%h;+ zcQ+R&XZW>0OOSMM`!l#NZkag)!x>D-^G8L$N%A|F16b{>t?5lF?uqw;jh`&&Fc zzfNthDhsk;D9*?t5aqKqHQ^*f0xh?HU-Fkbq(}M@J_rd3`K5HWrn|5@RN$v)PL7aW z;Sv$izMn0v{h_hZ6B$Qk#TG7LFJSL(Z_?~nu73YM1Gg#`rr@`6R43Q}p!E6v>d?KJ zTF2*N*#mwxFBCSPtl)EO=Gr4z8H*Z;nPNmpAY&A{9w8y&FC)vRhEpFrI}r(Hf`Woq z!;bqcF|n~^{FqFFGH7mIop*Chcyx5s=2IjIm$F4! z*zRYhNbju&X72|4XP@#1?XMz;V3_|NLTowjPG!5teY-RNobG+QlB2UC`8gVvUtFwy zc_r=oX!CG8!cU0tVG~M9$}S8Ay$aa9fPZU6(K)-MR3!O?+Oal4^>REL$A?C7Ji%=>&*A;(^zFFrA&#|W?2c)5s+)ojz-tcBZA3=Gd42MhPv< z)_#G{mPuii^THf#{%k%$-mRkrlV_@ThC*aFMQz^V81*1Jq~# z{yLA=R%d5l2dFVrq|0HZQb#5V9r=Te_&xH@;5!km;+#_;1l^xoP0XC;)g~?&6A41QF;tj9`i^7<#q{c z+YE-R4$IXu(XkP0czia#`*!688Uf;svrqGQW>n&ls3#i?r z#gxRZE|uNO2}($yZ1&kS-J?tPO}`-FT>koUapZ1@vxbI7mHgL|w+zlsdf#Kk>$M8? zQCl;u*)(kOBkY!MJ1;w6etli*?fsV$X|O*pVGEwJvcw$;`2t4^iK3PE-`I&sNakM{ zSwy6y=nkwLA0G>dijqP5ThrIKwze9kMf)Yb4-fAGjOFI!gme{mi9?<~9bzphEq#`o zn`}^|6CoZ0_Oifvtio@&oa8>iPB-Yiu2_yxbph9t*Rk(g{!qHjw8S^eh6WP|m!7|u z@o_~@K0x+nKwBq&`bB@X6d1phbfkM!Zi>Rd76-AD9TvZqmoGczL%=Rq=+Qixd81e1 z@+|~$GOHY+N&5WORx$w*=}4o}ty}a?bu4{-edBFU6~2G}o@H1u;nMx1+)U5ZGy$w> zu_OA*(P}};##^H+aYbq83JAnqm+{K^3J-=G?rq0>eA-#W$X-f_`+8G_coO3k1iM9R zkQDQ+<*mucJ7MP`iWjGQkQi+{k9}Gos77x!6%?10gi=va8L$T@R!dY?l^di87p#SZ!NnQU`Qku;);l2Eo*}U10`Z=FA(!)dSKA(ZMN)7$)e>`h70Q4RE z(?v$6r`d3O%?rF3sE}d(NBOjb$*J+`IoRdRKU@76^F`uHy&}-idclK+kUV3?X?cf!*m5YX7TYe19yd5m9sOV;? zR$f-7a=0~pm4N{R<}^|~CW44ex^xQE%z9(I%EQ~cb0AyB6Ckq+N{;u#YiV~cd~oRb z@rtl;nT3LLjX(mg&RAzBQMQcxlT;pGPE6Um?a6=L!tB_pZ=Ic;eUo+0Y=TAip7BuR zJw0IF94<-;wzFF@#Rukd_a`PKbfgIyRHA!m$}C!0hK7a&goHZgJ7Zr9Q1ac+45y&y z;}jFC#J2)M%apLAEGa4R_|j{l;7GCSPai@hGA5CTF}C9;^Tx)iTYBZT{|%{eh$|^66_KwAH2G~N zj9G@|c;aJ;GB z$PYDk@vqWu>geb&(%#-56t(H!#mRYBJ{e$H0$AkkjrS)k_IPNL%7eIhvjU&_M$Q9Y z6?PF?{7{JMx-^w(IisAM+`v|{p`jr{Nr|JatqqT(2*!HfSxu6eIr_YDkj6t%`lvjg zTP=BmN~`vd!jErTWo_pjaD%f&j*H?&0VFwD;u>6_FyL_7UvNc{N;#V?oSBN0RVkDe z6oH(Uwx{B8GZ0DUd&gVT%~b)rHaJ<&{F#$FQ<0mNZP?7r%>i0&8q5{OZ+DMY?%g zX@77ieSNwZ8nzO#ZVQ$2!ShKxou*)pl=t%e$wIA+AdZFKFB8+-$}ZPWK|v8IUQZq# z-1?;v4uswC+8_XAz>P%wI=J@UXG-5}wkGSYn3$LxZPa|Tl4Mz*%FD~6&Cwy_kKyF? z#FMq|f7iO7YTl|aecp2Vr$5bm=Bl!`_Jf}$%S0Y#wGPjQNxVtJ&ekpa%756El-j+c z1y#nf-)4!W<>)RkEEV(Ln)+!P$DbEM2LJ*v_h0$`cLqhcR_#DPKs!fYU*5_w%ryvH zeH{y9Vk#2rb_(MDArv9eoy#iaG$QJ!djtd!?Ao=@dwY9%zi<~nNB>O4YZi8Z5`x7d zH1u<>ogWkbQPDeYQ?f=|1AelAM|+&# zx2ggVww}oF9`jiGa&6;h18pLsNJpLXSf%SvX&`AgC#3*{5+D$WOvQ=UHH&qDvckex zT4sqZw}bq#^-E3w>^k?oP^-AC%;gU|sEY98CWh9azttJ=hO6xC$tT+%El&Zl709h< z2|{Ass<)5kqP}X>rS`{Ru_``3lKdD+_{DJgOBr`|LL#E8aCqF^DAK!y`v2GYJ;`jB z;zZW*FHWpG6+AL;(=fr1%H@w18&`5WgST5_H#Ro-*rN3*Wn^SjP$-0eK<^V~7H&bo zc%cL@CCHLOS$3sm9Y|X?{Y*d?f7i!=<-h`HL#8if@r_Ql%xG9yasE5p-u_Q;5D3xg zsi~=1O2k8lC_yil1%M6Z$7WzL6ZU!K$6L4%E>&85pa)PG1GIF=IdE}3UtZh*KISSL zTM{5acx&(>DVmDurg{5Y?d%5+9zaO)M`gwY>-R+v#JRb-Nq!2~P!Xo(F%1p=rF-fr zaB;#LkIJl|7)>bj3Uu(GO&aITz`(#MC3Ou@4ge{!msuGh5`c=e(uF7#Z_{VDM%AtZ8e@b9#0Lw#oDOk6mvycDZ7G^=}Y7NfqFJZBqsv3>NW)^FF5;h_9Ng7opkkGUb;eL%lBTjCDm3&?+wt=C)16LQ zC>(2CLxFpsN;^xj|==`o{9d zpW00o|KB6=!GVDjwRA6EzKnm)uN$eKFjSx>BRc~oJaWQiO(c6@IZI%ESuu&|1spS0rb+t;ZiYHg-}rDbI$tgbo;o7I=;6bE~Q|9jl*XXxea9rp5yW-`Ap z7gq;g@Kq%69YnE<$aMK5O5jSrz<* zar&C~wi;L1vpEDX?QKs_{!33M#mlK;;Soxw^R$26lBg*uktyuAA!DzL^cOFrjzelD zuv!^6KiNuLvLd$b{E(8Gy0DU`z^y1+to!ur^wi~dq$l3<&o2!~xkx8x@Ik;eD>>ql z>S{XRr-8*XX{E@~iS6h>rlqA#F&>PV0{_K{*?ES6-KjfFXh^TFmT;MNRb4?cPE zM2ZBo>u5;3sF1#TzjCGtsiO2;Dr~7p?+qB&167vvC;Z#vmlbLMz`t>Vj$blF^3{;~kdb<7e zG^FMH-@0+76@k9Kz6TJ+*|sFYV5La=y{OP=4}8CC9aA@4HJO#78=lOeWWk?cS~_Yo z{8n2whtHGK(`f-I$fe-fm%vTULFS!%$L#>r{GWX z@p!;p%zdUsb+hjhpBQhN8>5*50|TX;h3jc<0S`j~nQG{zIRg;RaS6=K$S_Je)Jz?j zBm56W2vmnED7HW${MM8EQGM4KP@jOP8UmNa!_Plf*3WS58kne23Ilfv6beNd8h9lM z&m}4&lQH%COZ`7YL_~yfwJk;4`9J4N%4KF|hJ^bRf)2NmfI6sMZ5dFolk;Atg0$Sw zW_(eQa>0;?N%}XfgnS2(#4K9h(?)1q>aqmD;RX5mghuZzqCZ}2CcTi-Sy@a`is`fM z0LA|ghnt$aRSI;<{;Hm^aV3O-**{VPT=H?{5ZyLUna@hdOL{VPLx5%U9DTru}m5sj`EQ>eH3pAYK$wtj8*tzF>Gr;j)QYAD8Qi~6Mn=BwY%*xR%MU*94SK{KPbo%?aFal(> z>~8?&z5=2%MNsYY+v&{J7W zEv@;brRXN8Z=TbKUUq<0N!XGH4AcIqx5=5)u;1g5?hwmTZTO8VqQc(!SFJ z544$=n);oOo~3luDf9kY#-utbQtp#i{Updg&l>0JmjERxEnO;(0*dr=BGdf#c9@yh!H>(l0mOjfkXb~Y0ph8f zB+DAmzIFx02mCi~TnXn*vnBu2)yi!A+glrq6qO#!hcqhv92 z_u<2bmpmwCKMUfsPC!+PiYN3_T0NawxSKdaqoZZHIXRxa7k)c*}Ke>EGeOJZwD=iXyX0bf+W7d-;p zN+bx(zh+5Y+1=fhzI`(7KZ`OnyhgB7@waudf=NYC1X$Llv7)?C)0HRAkrejm+v7b= zIUq)8osd%99NZ>j4^Zy=@|v>I2*$EDm0L<`pAR8(!15-f&8pA5JM{jX2<bXz0feC^vbTXo zI@3~tn*#9#NCoG%U?T-(XB;mt?`PAh`c;Q;0t{@JJ4G=c{-~cw}Xb&v$z`9=p;crDkOCgD&MW(7XYS^Zsc7g5Lh2(6!lm z*X+{2&S@a9GlL{sAofvcs5)u^$hvKjBVM4f#?vjF7 zce1|bi%Tyu8ua6Cy4@~_7b&4gST(il{(qyE%z!K3Jmb*_TrY1>G0PH|1TDX_zyAwc z#RbvGQS@cQ{!l~W=x!=&F}Qho4GbBVno76ZJbRYhDWQ4{lm12L(6eSsB_4&hVW%Nps;(oC3&6E7=5rjudNb;2U_-D6n-FjSd3Xp{t+{&=ie0R0|dGf=S zoJaQ{MO||7fJ|Kc!=HWjZ-2D@V!5ywR;5cZMA83XViWO7Hu|+b4 zj|mh$(bd&$v-(7MnXJVM<>uvyG71}Wf;}!udqfaGy-9T3OhITRj~1p4k4s6(03rE} zTGR5!n{TSC^<5@wHGsVsF@2p51(52vXtQw9w5uY?4|jIvZaw)53qII*7e7$_;`eW- zXIWW7-N|gFV%-dyj#9$GR4jObX&o~)La}?PZ)zY+bF)I8Vqo>r{;)2}+Sewsms?rv#hKt#Grq`ThX&G!Mq z+mnq%y+b2a1cdf))mN`C@#E z0KY$Tl>gv@fPmBY^o5wribDbZNaiY|>#FHs;p$=HY>wdJ;lW{LZ{uQS;%Ls{;B1-s zPmCM^ffhmG-5V{>tb=S1Un28|!R3k3Vh4GQ_Dgzs&U|?}l=*Db!>@$G!@GymxxwD( zIg6)4BWmi?Z}UCFLkY3^ofVm0Q$_^7OhX6?3Bl9DP!Mpd<()W;9~wZIOIlQ6G+(Nm z?TFFOxgO=p_1e3<#jqwteSu+sc!p*QfgB+IMr&xLcu|b>=K|Y zOF93bJ1v|~GW;CP9*waaD)I(iCm;CrE1ozbW7xmNR#tU&_1_2X!NI|=U%#g1f&@*xfCH?g`WNY187QJ#&SrI6ai^ng$Jq;B{hQU>Kil z3=PMS@uqQ^AVuY08F;FzO$rb7jM_APj95Br~e^(*ro}ELd&6h z2wMoyq1o;p16fSE4kGE#fs_KaX&0ls6F&~-8%Gin637>gT3{nI0e9*hcPAq!88)z= z#47y zq3g4dl9iEZ-kQkeBqSuf_s-20a*~#KygRM2nT2sku|(C-^!mC*A#7pM87#JXc-YO= z^j9){AapfMJ#4?siHV7EWsX`yu&>yUHZj5c*7cGugqoOm(|fP1UGM)6R* zc*M34M>;bt+7gtXYebL5D}&MWhyYoGH%ySNU#D^ z0~;ddRdlKfNnuofgh2ex_hu||E$elwjDuPS^mi(wjZMh$r6U}`rOUpjDzy$3m>*i* zE1qLx&uBqZ&|0q73sVuDcc+Rha`j`I9v^NO2Qv5L!Cjc{5QxXTt$<&D6tTs!MskhI zwkjDu{;CFqADM>f*CEnIqMC%&h49P^75IjV;9?;-m@dc81tbzTg+M&h3Mlj@gWqPf zBOBV^;dlGV)3a&BqLvCJ!^dSRl{rd7$ouT`*rv0B;Vi}Ih>E*=!{3GG+H%)x-K2oS zXVH3F>{0{${pp_zGHSg}Ey(cj^2(q$*v92U&&s3n|Hei|Ih%j_B+p#r+H8Qmw`nmL zM>S5alij32m?cU9kMYgINXb_Gutu#l?Lo`yGB{SDYNJ`gnwWI1D|=C@Cu@-#uOX25xEI{{AAEh= zUWOc37hBc)R@_HJA8IJ!tB#Z9I-Cc8=V~Q)TO^2B#Y&g*q1L{-y1#$i-(Hqyi_Y#z zKk6|^O6CS3qgyjGGm~W$_alUEN_Kj3*uY@>_zrEyvgxbjBCD&bZC7j2P}u$#E|bM9 znfhi!cpdkIS%`ASoc125< z6Lxbk^4oyQkL`8fMZ>Q`VV%H+H4y@ZdKVgX`d4@yE>(QIJKQerSPGYp!0i2P-+AvY zalMgfYB*~asC9X}+3D3-Z1wGQYs`icVj!#qF$X4s7F)dyJhs@cZVH*pD6aQ8pV1K~ zhtZv^MRS6k(sEVa-I}9m5^ZkNy$$R8_osfJVC2^$-~RyDtNT=5xKgj&mqr+lYsINOTEw3?b>p9kAdy??mubg0`W z{_^F^^?^e`IZKo?t1Rp}7U5S9LYBk-nr~;gvzBc#ejOhV(l{htf`y)b)j)gC@v_?x z-fmnw_mK7N8=YQ#3m>Y2pI?t>k0v35ghYJC~YmTKfLjBs1a22YPf z=@2h)UU#T0t2fJ%@XLsadCtSj>(p#uj(Z!$Sl8;hEB*LzZ=*7Db-do+;iHweOX44FE1Djr51o%a3|5hL~c5?E)Y|M*Q9V$|IXFvdv%(oVpk%cBvkza{w z_8m{enxl|zlOtU?E={q^_V{R~VEoR`=Y@SCrAwZbNTwWGUf#r{BqDo&PFVD|)3dWx z3=Ak06ciS`wzFM#CWCszacez~O=aV$$G(54#lSe?Gc^>Z5_R9u$}tz=70xoNbM(IW zyW7cwOS83GlDh>G+}Yo`#4^ni9^)P)hU#qR5<;;^r$Cx#8A;Me zNKaSL(n=aEE8W8*B6@dwaZrDDcJ^6j_MEGA@p3%2*y{Vo_6)F3JZkE3n#U_SQ&Uq8 zZmszSyWQW_uMh^~X-b-!ntDz;9&T(JZ0CB!FBf{#xXpIvYMEw@0>nEX&MU7^MmXoH z&BAB(4#qN8UmxFWllapzqyK5NiZKUl)L`OGGwO7i$p*t2eigS#w``Etvo%+83A&iKtiHi%bWND{_ zVV~cN!5G(JE48Mkw)XmO?F3jHA~M=!tH&W`XKHS)Q3JFej$riuJsyCX${Bo-WV{x5 zFLWzH`Dc3U8w>VZ7K>jin)v&7s6n!m5syn>3|Nw%galGSX)a= z%Y7o~J$}1tE-|M~$>AaQ*ER_G%&Dok${aIKrs5}#KcEP~qvN}ja zHD=>IR}$7piPZua6e7aq0&-ZEDwBI_Hq5Z~X7h^wjXF_b(rYPqZS54b(B$|$fIf=` zS+1wcH2=;w;?kiIGO3<uy2IW-hTTz6Jx)!};}rr#4W7=c6xB2__xeHjNEyGXM{HL&_` zdA@21Y~S~4H9SkiEs9#)+i^SF!<+J?(!ONIZaibfRYXBWsU$q0g^=WE3l9QK~8`5_H6kD`yFP1Kkq=A>q)E zA5AN@z&a_MM&n`F6cKT8TmOYq=X<$KTnqK~#N5AN!Q6apRPN1-kd&1bqXgeEPaoIH z4_^68wk$Y>{rowb4YzRiIOZW)cyISqkrHCl#UC3Rw{#QpPZk#W``*P!lq?(^+Q6T; zG=o6iv;ghT#>U3cUU_AW|GhT>0l^jcCjhUA0MR^EO0kn4uinY&bkwJ|M!x@v%GT*V z{1A89S&pL`^ z*IB{CK(KT9T-fHut(LELeKxVmU<~{sAZ4A=;I-J|Cb-`MyLlBnYscfTE#!T^Cj)S{ zu=m;Pxw$#g(b;UtfSll9B-vPU$a>@m*rn6TcCMF26~Cdez)6f8Tu5b#-x+n=L1=s(N81_FseJtbGm- zl~!tg{!8E`g;p62h#bmaPIDIR9~VKco3W1;$^$)lkyD zHZM~8!ivVS2{H9=yTz*6!=&XBjOXszByN(N)~i9>MwqH$uIoqN3LyTC9RGU3oJ9M( z?&?Qz4R=nfj4;^#6I7T7j}0C=hbnvQS2Ui0xkoOXuY!H0B~Oz+~F?szP9{PIpKdBI%0^kg)09)iCeU?Cfn751a~A1~8`C=Sa1| zKf*IeI#yR~UKvg2-D9n6)3{aK|Jr%CiH`*FoV3>dC0cwQ?CzdSYUJ5%%Us_x!r2*9 zY$bA(aoesG_85Yfi;9Z6=n2v_sNEb=_WHg8mJ!x*W1T~WsfGjFkRvHRUWh;1 z;rQL|<5BcscW>Wsr>t1i=Yq|!#q}o^7FH@UeDToeM1^_NpalMb$K&4unq)deSNF&L z<)Ts}MzW-Sy+QkC&Pbk2#PD0(@$->)u70ba?=cghfBmV;}8L@xm9cIbR8MHKI zzSV>Ca=|4&NuvYJ_x%H}TJ1;_o~hyc_mfv3Ke1wSBOd}&r-^y-;GT!4R&*zN$8(tc zw!|dMXSltf`8;`REtIVqhD!rGoNwe*g4gAAp_Xln#k%Zi42vo$D%QX{AH2sj2~ux{ zbHshF?$1P<+hTiMdPlX*)TxLNmE?9mWsiK?iXp>>xj}LUseNKVEoaFnzQ-jc!(p-i z*fNx;Vec+P2^X|qC{@iAP`Tn&P_p}$kkB);K0pB7tX>t#-r9QSr0e-+{(V9E^`9;J zp59)SXrI&2%8r|;+rv&8<7Y=2GRtM-YyJBnct)UYc=z3~Rhv0CsJY|mIs7{DOuVmR z9RiBroWr&XZ+iGmmsYQ(-R&J2RfzO{1+&@s@!ca^Faob~kdAOk%7OzQ=k?jmt- z<1@O4MUZdJ!N?3M=ww?z27)WibquBM?$lRY!R&MI0Rf*ErUBQ3B&OshN|h6Q3r&3; zz}=W9j~f{s6|1oN!?hApa;PmBx2?&%Sr71G{}(jusfXL;#~Zn_+^3evOg8o%v)IJe zK6@{}tF+xwKYex_9S1C%?;E>*LmGRT1_;W&AKzZpkB*KaslXZ4BUke*e7@TM;7?#2uFBD83L)5+1K70(iVmKDFoNt$Zq5^@oigwy6hvRJ>+2{sn-LGdeaoJ&orIqH0!Yo$NWq z?d9Qi!@RxOT3=+ucRqn7gvyVoi0A9e-8~a^7-1EHd&-B**YnE{1BH>Zj(1m1n*8RL zmb@-o$`%tju_K#*pjLnIRvg^|4wZBd;x&dnTn*!I9&QhBJ0EWL0m@Ny-JL=WnDy0A z?6|+sr}n=x*&5I8JuV3Mbj|y8?Wy28Rtvo?F0#j+rNx_8RoP~g3d(GiF5&X@@ga{+ zWuv2~PXgJZZQQ0kUg9Q#!oI0n99Au*C`?hR9_1Qf2>sVpARV!+#BBF;&Y&TL8TcI3 zC;zi~4@NZA9)}+vPIDiR9$H7_B~lb?X+v*0;a*iV#r=J|Q#?!k7%@W!Eh}lr|K>Ta zSX7)p*glV!ye85tRxWcp1)w0ZB*$BExx=qD|MhTu3+xf`_qxi5hYqcZpk?ML9{t(# z4g_#=@PSy0l8aqY!K%^=pM5QJO_0Rq4F)Y^{K`=5)6&86e^O>XWd}^MtUbI|EY)(|HiOQ@~A~f zgGB3<$C|e&are3!-|71%JfC93nC7)`V-xyk++9ETAs(j3E`zh+j-81{#Od`ec(?<30!*Q@Ib5v%jAGU zevxnl_$iNmaVt@+vQ7>S&uZl2Uqc`1gF-7k)<6!!md{U3v6-nUK%*QsCj2(PVgN#= z#6iai|58{;{}go(_qX+6YFk#cdwasCqL0dk+^{xC^^Rh6JmSQj8igdIqE{d8=STUA zO3+@l{;Vis%H(p&Dnh@tC;8l=S}l1R9gb+}M8KZ%4f(A1vw1A&67Km__8E)nyl(!( zlTK-X`49Z??7+XGF7@1&r#XIIxb!n;cF34<0M*X%H;QY++ejSC=#@d2l?1%PuDp~< z5tKiMEzA`%Zr{YfLk({NB$GQnGOKjcAk?AjA+5e(XCwWJy`jh)Zm zJi(2T>+YOWu!)e&NDZ$sdGPqU9sGuUWo{%^YCKF)ymS`_>Aqh-DGi#R!B@&`UY#`7 z#Zv2br5M$#JB=l)`WR;0BkN0^W^KbiZIdOMMv#I z&zE1Bm@P2hSD?;VBqSlr6x}zc$zwF{?1}R0=Hlq{#5}t)Cbr*su2Wy;)u#6`lA8fv z`%7s|L35SZGlJS!bL)Hkt_}Oif)6vD6}_tS?dX3z>)wZEVsuiz1b@`6?x3QO85$e{B$%(H|1g zI{%qY2VWpG1!qqIG{CfSvQh&bCtbP z-bI}2ALz}i(oT8!h1Wf}#@>+5-m60%y4wfedA$%$)601`HYZ)6hCbO&#s@yBalNjZ z?|b5X>Ln4P;lY4I!1w%yUEA=^=OHCwT|4gl@U5v>^d8H>=TydgmU4Cc8C}n|SCvDp zH1~;^IH-B`LnJ>F0rNQhl|9NyC!ac9jn?|@cQG}Av-!eLAn>>RtCp@#j1gDn}dkW~Vn6h*h7G@L}s)QYj|XK|ga@CAle#Xz6Y8_PzcK3-U=M*sgKf zk#9BkNfS^1kBP@{U9+$2Yr>5P{X3$~nu1A#;z(uQ`1E^rzkHan$F`$aA39Qhjk-l` zm>jzv6smAV56qOX%Jfw$ERB)pjbD7&GZnO-44~rKYRC_!E&sg}eeQthamM#cPn&Ou zs*wDi(PQ}Hq)CNzufH>?G4aE4Zs~kJ8YS=%7f5G&!#Glmth2>`trSJ(>&ac}4M=L57p4WeMjhZfPXnddCuk--Z@p zb8Evy`1Ih2cmpncUyWz+?P4;x#UE2IU7rn}((&MNa4x#2DSB?>Ewmn_Mxd%`eHxY~ zZL6W7UP2sd?rf`6&`m(-)$0wx3FFQ#!VTgLb?XqC52`R37>0xoEVR9Alm4$Q`Wi!m zM_&nY7Rh5hIH4I^WMua?=GiG#$*PAq^E{gJpeLs|jW*4f4)xCGHBBm8I0Y2kGn7Dpt zp7TBxPq#&5v+`|hHR^MdG(dhe$fm0XE4p-V@y6}Hm zGH|ZNM@O;*8t#O+n|F34kkqNqdkX$TXomPqSTWzG0i&C!bs@kvrQn@$|<4 zKKa_~pxc`(0S5uzfl|bMd>Z~R-R8YX`WsjK4fyLUWAtE?r4$ppWH)C29k_7NNS{9C zd32t)>b>``8J?nUm`k;<=CbZ@t>@?}qpk=qWIYV(oCNgu?J69?McL|`29DOsoJK%!-&!fQTLXu11>8q;-yG4=ZJt zL%MCq>+=iIRsp=I_g>Ss_~*|?T!~F=b&uQUc*s$Mb@~#*zWLO_s+4sQ*R0N_l9se- z`UJ;my}H4r*dwmgcn+sV#?5mgpJR0J>C1hQ!kbM34)evlWzSTZj_1o8MZYa zi~v1}8^f(H*WMkG+&YZ8SVpa_vW*=@{8tZ$RT~zjTYMTkMR(jd_DfyuYBDOhoGfLx zO=gq(yYcKxK5#)XxFBIF+c1|$KYWOd=uo!|%O=WP#Av6Bz_@XaM|th}8}08|g5atk z#`fCs4X5K4;)@OJlwIF(ss23c$IUoJ$7|G8`a3512lh8LY+lDwj3k53@ zTP&I?Fv{dZAiv$+M?V2&K=4xXYw7qPVF7Im+oM+gpro%XOlF8Yg9_@1v(Y83-= z3RPJ0AQ7gLYcLXWlk=5h7*2auTG|UR!23K4qXHxRswys5!zV>dTbtMv94_p6L<JH=1 zFL!wLlpKH6y;%94s&UDiwLmuZB_H2(xtXQ8dA0cn2^gMrlja7)o3mh zjTYXP$#7y{*hbOp{gUUUZt{P0-6v%wISKv!{WY+miHX$H?TJ)q{*Sm-=z@RZe-*(x zL0KxKwUxP{GO^!kBb~H``y%J%?@sqB%; z&t&=2@!1~S;11tV-y(w{LxVPRqClZr+~sij%;vPFnR3;e`CBi(P6jjfGN8sj(? zPdYw2ns?reIDfyzcO?j&p4PID)-de&!T2Jyw(2NM3IY)j`9BvX#YPs?<4P~7U|ytb;8djjc+spgxP;|DCb zu-}dAvz;ynm5wkHk{OFEJn39vm*P3<6U-7gb2v6daT(A-wWRxTv$H|-L!r=;Ct`GX zWQ2EK@?&CF7TbY)s}f`;+xYyDZBNh%NO*iM|MAZYN}P71(=^?BT&DY}v$KhD>tg&T zezo$&;Sts|3Qd>#48~xp9pa#TB)j(fVFNA7lW=NDk35+Ehh&S7V0&go@bRY zI58@%P84$FOF=2(8nM*wqr|*_5Qz^j0)3v1mcs zaV0nXtoA@|$rcU48_nP!v8AdX0Nlh+z6vtO_BlOrWbM-_IndhkIB)#0KR!N&NOW2M zX`6PVDuGAB_T`yJK9Ujw%GcP~ICj`w6LLz>wdwp5W+=+e%j<&en5lza^SW2Ddz<2^ zw{{w&^0=yyGMokW=bLYIumt0hEKv`bMlT{5{Dg*6dq+IY@ONLw10rBJiFq&6p~I^H zw*SW19Q71+#I=JPX9dX3j+IOR72|uonfgS!0v=9lUr-tY6BF|_Gjka~Jjf~T@oG>q zIFL1{qTW4^G0Rwluj%)-L5R+Rd2ZQh|%Mp41mmQCE};+;Ct)YR1P zMh6-VMQLg2G$E&#-QC@#P(@>7S};$Y72E-Z&83TPdc|58lZ?6>FH7EP-J9ee2==( z!Q_M>aJ4s_7tGw1hQDbz?@ZFIt*v1^5u6Iv)+}Jy@$|0EFE+h~GY&+HPoL=hZZDE0 z@6X_lxvIZZ!eT6=zb}$dJ;`XmT>9->GC_Mv zu=)N~N5`w9RZJC+y=mr;0rzhHrAr1D6!_6++pXX{A07DL}*=WC*CgR2d)F(jtl?a&l%KD4F8LZTi8T``raWg_t zt}b2C0jCwN7V7(We+izAz~%l|H@=y3B1`xS+=?E0#`i4u>sKkj-UK|}E(?47Gnl)* zzd0{3A4vhzaNyy(t?lT~wLS1YC`Siq@{;JR?#KU`k(XnXHnK?KzHg^8Ub#r`Xm}m=GJ9pgY zZx?Do7=Qn!^S7KFoEIvM7Go?Oscwdr6FK5d1{@iJ4sj*^8+MJ01A&OhpW(%0sOHTj zL?L)}pMUo3tB)QX9o0y-vveVW`if<;(p;uPb#i(-k`V)D(`QXQgkg z;B(j>Ln&O-vx}mZCFYd?*c<{XsxiQtssRqsZ%XeU;`+RTpwoAW?8_`9c{Q~XVBn9W z1QZk_KqB!(XL_zhGQ9w5kFiWaCLrYi;tFu8qj%S*yTE|4!((G(=ErOOC}?OWzs=%* zLX!`zR2RNTB;~Y*dI00)&XY5m4n#L9%oOQ`G?z zQIKugfa+&sbMqJ|AXCWx0X5PSCl83!>CypLm#ZTaBAKCCxBE}A0k-O4z@iF3wF4rp zS3*t`i|+GMn{emNOrbN&+!>az1PHG)f23fk60fA)u-O6ASdXVyfSDLSg$)MHudO zKdAS0sj$jggl5uB+#hha$bjsUv9mQa(A3b-@O-%59s*|R4n__9ISr&?AY=-YJ_?3j z9xhK@pKe3GS5fCyTZ=45a56KigJ801z(J6>J9^8*!}DWgWc_L1;HZG!#M#A#nT16f z;|(wwShs{k$6qzcA-ft~{%i-|g4ajSa8`dDB>zS^=6JH2X#LO*8O&p;t9 zV-})&&dnO_GnOXC42&n)$v{Qbvzvnx%q=#L&`uP1b(gh6$%{$I-+G~kPMPPPK4>glarF3GW5aO zE?ksPg#r>jMa~X@xoCezG5bQ5$(P@?mhp3XcjM?ZzEZNXVSt)&v9PJGdaV|c|2i(y zzNKu6N_cdMt+1FP0+?y8Kn^!G|Fxt^eH7asof^WER2%Xy1peN0w)^TV5fJ!(Uw9v`r08|F(Cp#?lp$OuJt(#3 zp0P;@pcol4GBUOlCUv6KjQ;v$qE6<3MRx`S1DOUoUK2f`Sc_xBw__b?0I$$Z168f{ z3^aNFceR{n?bgt3A(H17Ftb#z*OG!bo59syMxrdgJCxv)v>FZP|)H3Leg%KMT zRsi=$$V?Vc(9K%r`dKtg6ebPqrRjY1lKEpRUZ(%q;2;@kjCLRjgaLY|boe~}0Yrf=he6Z(u&^*| z0FaPyO;xaT`S8)ope+<%{cg^FF8N&=mRadlWE_B`l?=kk&1Bl%$i&XW{3l8^BJYzP z`cpi$xB(Uk+gIQ|mzi@oltQ$RXC;zD+7)`u2mlCr1aAA{kdUaLP;db$>p>t#$mA15 z6o0r{=aG>3xV5#VJ^v{^J^dt_v*Rle<|PB!)IYs3NK4D7gys^f)dA~?Sx#@$7e{D@ zv*=39#7}9t1*j_v8m!>5A>rYKWMrUb0ZGi)#KgDW-Yv@V@{>xM8~?qI(o}3VRJ=x& zjS7V`<|OvZfKIiO4_y}7FK7u!7FnXE6lc#d3}<^o@LKR*s;MW5rH;56D0<-uq1++Z iqGPH|+p9l&#Ep)of}kLy`VJ;-9Q;2XLvru{ literal 0 HcmV?d00001 diff --git a/docs/ising_qaoa/triangle_desired.png b/docs/ising_qaoa/triangle_desired.png new file mode 100644 index 0000000000000000000000000000000000000000..0933881d2b29712d802c8a2b861d51906b96674b GIT binary patch literal 5774 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 literal 0 HcmV?d00001 diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index a61e10f..889e947 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -1,4 +1,3 @@ - """ Finding the minimum energy for an Ising problem by QAOA. """ @@ -89,7 +88,7 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, :param vqe_option: (Optional. Default=None). VQE optional arguments. If None set to vqe_option = {'disp': print_fun, 'return_all': True, 'samples': samples} - :return: Most frequent Ising string, energy of the Ising string, circuit used to obtain result. + :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result. :rtype: List, Integer or float, 'pyquil.quil.Program'. """ From 1b5d0c5747c403e98a0592aa5707e0e5b3486bda Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:20:37 -0400 Subject: [PATCH 13/38] changed IsingQAOA notebook to new syntax --- examples/IsingSolver.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/IsingSolver.ipynb b/examples/IsingSolver.ipynb index 4a43a7a..4d17f8f 100644 --- a/examples/IsingSolver.ipynb +++ b/examples/IsingSolver.ipynb @@ -20,7 +20,7 @@ "metadata": {}, "outputs": [], "source": [ - "from grove.ising.ising_qaoa import ising\n", + "from grove.ising.ising_qaoa import ising_qaoa\n", "from mock import patch" ] }, @@ -68,9 +68,9 @@ "outputs": [], "source": [ "J = {(0, 1): -2, (2, 3): 3}\n", - "h = [1, 1, -1, 1]\n", + "h = {0: 1, 1: 1, 2: -1, 3: 1}\n", "\n", - "solution, min_energy, circuit = ising(h, J, connection=cxn)" + "solution, min_energy, circuit = ising_qaoa(h, J, connection=cxn)" ] }, { @@ -86,7 +86,7 @@ "metadata": {}, "outputs": [], "source": [ - "solution_2, min_energy_2, circuit_2 = ising(h, J, num_steps=9, verbose=False, connection=cxn)" + "solution_2, min_energy_2, circuit_2 = ising_qaoa(h, J, num_steps=9, verbose=False, connection=cxn)" ] }, { From 887ff720e53614abe30312a900709045b87242d8 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:49:54 -0400 Subject: [PATCH 14/38] cleaned up doc string for better read the docs later on --- grove/ising/ising_qaoa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 889e947..44558d2 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -88,7 +88,7 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, :param vqe_option: (Optional. Default=None). VQE optional arguments. If None set to vqe_option = {'disp': print_fun, 'return_all': True, 'samples': samples} - :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result. + :return: Most frequent Ising string, energy of the Ising string, circuit used to obtain result. :rtype: List, Integer or float, 'pyquil.quil.Program'. """ From 81c80766c4bad8c73a31cc8490bed4f69208d736 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 15:58:19 -0400 Subject: [PATCH 15/38] fixed existing tests for ising_qaoa() --- grove/tests/ising/test_ising.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index e75ccbc..17c10e7 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -1,4 +1,5 @@ -from grove.ising.ising_qaoa import * +from grove.ising.ising_qaoa import ising_qaoa +from grove.ising.ising_qaoa import energy_value import numpy as np from mock import patch From 46c1981f715ca6e658cb5c29f6122011185abe38 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:02:32 -0400 Subject: [PATCH 16/38] new tests added for ising_trans() and energy_value() --- grove/tests/ising/test_ising.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index 17c10e7..e75ccbc 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -1,5 +1,4 @@ -from grove.ising.ising_qaoa import ising_qaoa -from grove.ising.ising_qaoa import energy_value +from grove.ising.ising_qaoa import * import numpy as np from mock import patch From 1c3722b492f51bfb6595d1580658ba3ffdd4420a Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:15:23 -0400 Subject: [PATCH 17/38] read the docs for Ising QAOA added extensive documentation for the Ising QAOA wrapper added images added ising_qaoa to index.rst --- grove/ising/ising_qaoa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 44558d2..889e947 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -88,7 +88,7 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, :param vqe_option: (Optional. Default=None). VQE optional arguments. If None set to vqe_option = {'disp': print_fun, 'return_all': True, 'samples': samples} - :return: Most frequent Ising string, energy of the Ising string, circuit used to obtain result. + :return: Most frequent Ising string, Energy of the Ising string, Circuit used to obtain result. :rtype: List, Integer or float, 'pyquil.quil.Program'. """ From 944dc8b3e4e8135d60c7249e01cb8bc36cb574b7 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Wed, 28 Mar 2018 14:25:38 -0400 Subject: [PATCH 18/38] ising_qaoa: Make Python2 compatible --- grove/ising/ising_qaoa.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 889e947..fa54005 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -25,7 +25,8 @@ def energy_value(h, J, sol): for elm in J.keys(): paired_indices = [(a, b) for a, b in zip(elm, elm)] if len(paired_indices) != len(set(paired_indices)): - raise TypeError(f"Interaction term must connect different variables. The term {elm} contains a duplicate.") + raise TypeError("Interaction term must connect different variables. " + "The term {} contains a duplicate.".format(elm)) else: multipliers = int(sol[elm[0]]) * int(sol[elm[1]]) # if locality > 2 then add more multipliers From 52f5b8b529ed3e57a567e6d5ab3669ce67336c54 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Thu, 29 Mar 2018 18:09:00 -0400 Subject: [PATCH 19/38] tests: Do not use start imports --- grove/tests/ising/test_ising.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index e75ccbc..b03b44f 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -1,7 +1,9 @@ -from grove.ising.ising_qaoa import * import numpy as np from mock import patch +from pyquil.paulis import PauliSum, PauliTerm +from grove.ising.ising_qaoa import energy_value, ising_trans, ising_qaoa + def test_energy_value(): J = {(0, 1): 2.3} From e4778d2e6f8b7c8f7506afd573f93456834a3643 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Thu, 29 Mar 2018 18:18:29 -0400 Subject: [PATCH 20/38] docs: Fix incorrect import --- docs/ising_qaoa.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ising_qaoa.rst b/docs/ising_qaoa.rst index ccb451d..2ed332b 100644 --- a/docs/ising_qaoa.rst +++ b/docs/ising_qaoa.rst @@ -34,7 +34,7 @@ In your python script import the packages and connect to your QVM: .. code-block:: python import pyquil.api as api - from grove.ising.ising_qaoa import ising as ising_qaoa + from grove.ising.ising_qaoa import ising_qaoa qvm_connection = api.QVMConnection() Next we define the appropriate bias and interaction terms of the Ising Hamiltonian @@ -173,7 +173,7 @@ solution. Hence, we don't use any bias terms and only anticorrelate each pair of .. code-block:: python import pyquil.api as api - from grove.ising.ising_qaoa import ising as ising_qaoa + from grove.ising.ising_qaoa import ising_qaoa qvm_connection = api.QVMConnection() J = {(0, 1): 1, (0, 2): 1, (1, 3): 1, (2, 3): 1} @@ -223,7 +223,7 @@ we set the following biases on \\( \\sigma_{0} \\) and \\( \\sigma_{1}\\): .. code-block:: python import pyquil.api as api - from grove.ising.ising_qaoa import ising as ising_qaoa + from grove.ising.ising_qaoa import ising_qaoa qvm_connection = api.QVMConnection() From 7af394c6efebe8b756769060b0ee915e7172e368 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Thu, 29 Mar 2018 18:19:36 -0400 Subject: [PATCH 21/38] style: Minor PEP8 fixes --- docs/ising_qaoa.rst | 2 +- grove/pyqaoa/qaoa.py | 2 +- grove/tests/ising/test_ising.py | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/ising_qaoa.rst b/docs/ising_qaoa.rst index 2ed332b..92aeb84 100644 --- a/docs/ising_qaoa.rst +++ b/docs/ising_qaoa.rst @@ -227,7 +227,7 @@ we set the following biases on \\( \\sigma_{0} \\) and \\( \\sigma_{1}\\): qvm_connection = api.QVMConnection() - J = {(0,1,2): -3} + J = {(0, 1, 2): -3} h = {0: 1, 1: -1} We can now run the algorithm ten times with step size 2 to collect statistics (this might take a couple of minutes): diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index 8103aab..612f48d 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -139,7 +139,7 @@ def get_parameterized_program(self): cost_para_programs = [] driver_para_programs = [] - for idx in range(self.steps): + for _ in range(self.steps): cost_list = [] driver_list = [] for cost_pauli_sum in self.cost_ham: diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index b03b44f..a9f36c8 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -13,8 +13,8 @@ def test_energy_value(): assert(np.isclose(ener_ising, -9.9)) - J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5 , (0, 2, 3): 0.5, (1, 3): 3.1} - h = {0: -2.4, 1: 5.2 , 3: -0.3} + J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5, (0, 2, 3): 0.5, (1, 3): 3.1} + h = {0: -2.4, 1: 5.2, 3: -0.3} sol = [1, -1, -1, 1] ener_ising = energy_value(h, J, sol) @@ -42,7 +42,7 @@ def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response cxn.run_and_measure.return_value = [[1, 0, 1, 0]] - cxn.expectation.return_value = [0, 0, 0, 0] # dummy + cxn.expectation.return_value = [0, 0, 0, 0] # dummy # checkerboard with couplings J = {(0, 1): 1, (0, 2): 1, (1, 3): 1, (2, 3): 1} @@ -56,7 +56,7 @@ def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response cxn.run_and_measure.return_value = [[1, 0, 1, 0]] - cxn.expectation.return_value = [0, 0, 0, 0] # dummy + cxn.expectation.return_value = [0, 0, 0, 0] # dummy # checkerboard with biases J = {} @@ -70,7 +70,7 @@ def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response cxn.run_and_measure.return_value = [[1, 0, 1, 0, 1]] - cxn.expectation.return_value = [0, 0, 0, 0, 0] # dummy + cxn.expectation.return_value = [0, 0, 0, 0, 0] # dummy J = {(0, 4): -1} h = {0: 1, 1: -1, 2: 1, 3: -1} @@ -83,10 +83,10 @@ def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response cxn.run_and_measure.return_value = [[0, 1, 1, 0]] - cxn.expectation.return_value = [0, 0, 0, 0, 0, 0, 0] # dummy + cxn.expectation.return_value = [0, 0, 0, 0, 0, 0, 0] # dummy - J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5 , (0, 2, 3): 0.5, (1, 3): 3.1} - h = {0: -2.4, 1: 5.2 , 3: -0.3} + J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5, (0, 2, 3): 0.5, (1, 3): 3.1} + h = {0: -2.4, 1: 5.2, 3: -0.3} p = 1 most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, num_steps=p, vqe_option=None, connection=cxn) @@ -96,7 +96,7 @@ def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response cxn.run_and_measure.return_value = [[0, 1, 1, 0]] - cxn.expectation.return_value = [0, 0, 0, 0, 0, 0, 0] # dummy + cxn.expectation.return_value = [0, 0, 0, 0, 0, 0, 0] # dummy swap_mixer = [] for i in range(4): @@ -105,8 +105,8 @@ def test_ising_mock(): swap_mixer.append(PauliSum([PauliTerm("X", i, 0.5) * PauliTerm("X", j, 1.0)])) swap_mixer.append(PauliSum([PauliTerm("Y", i, 0.5) * PauliTerm("Y", j, 1.0)])) - J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5 , (0, 2, 3): 0.5, (1, 3): 3.1} - h = {0: -2.4, 1: 5.2 , 3: -0.3} + J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5, (0, 2, 3): 0.5, (1, 3): 3.1} + h = {0: -2.4, 1: 5.2, 3: -0.3} p = 1 most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, driver_operators=swap_mixer, num_steps=p, vqe_option=None, connection=cxn) From 928e6cd1f261796bd813665f88cea557005c93ee Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:30:51 -0400 Subject: [PATCH 22/38] introducing new argument ``embedding`` to QAOA the embedding dictionary is a great tool for QPU users since it allows a manual mapping to the hardware using the ``embedding`` one can avoid dead qubits and choose the qubits with the best specs for execution --- grove/pyqaoa/qaoa.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index 612f48d..a9ffc49 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -26,7 +26,7 @@ class QAOA(object): def __init__(self, qvm, qubits, steps=1, init_betas=None, - init_gammas=None, cost_ham=[], + init_gammas=None, embedding=None, cost_ham=[], ref_hamiltonian=[], driver_ref=None, minimizer=None, minimizer_args=[], minimizer_kwargs={}, rand_seed=None, @@ -45,6 +45,8 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, mixing terms. Default=None. :param init_gammas: (list) Initial values for the gamma parameters on the cost function. Default=None. + :param embedding: (dict) Dictionary mapping logical qubits to physical + qubits on the Rigetti QPU. Logical qubits must be the dict keys. :param cost_ham: list of clauses in the cost function. Must be PauliSum objects :param ref_hamiltonian: list of clauses in the cost function. Must be @@ -76,6 +78,11 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, self.betas = init_betas self.gammas = init_gammas self.vqe_options = vqe_options + if embedding is not None: + self.embedding = embedding + else: + # create identity dictionary + self.embedding = {i: i for i in qubits} if driver_ref is not None: if not isinstance(driver_ref, pq.Program): @@ -85,7 +92,7 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, self.ref_state_prep = driver_ref else: ref_prog = pq.Program() - for i in qubits: + for i in self.embedding.values(): ref_prog.inst(H(i)) self.ref_state_prep = ref_prog @@ -242,9 +249,12 @@ def get_string(self, betas, gammas, samples=100): param_prog = self.get_parameterized_program() stacked_params = np.hstack((betas, gammas)) sampling_prog = param_prog(stacked_params) + for i in self.embedding.values(): + sampling_prog.measure(i, [i]) + classical_register = list(sorted(self.embedding.values())) bitstring_samples = self.qvm.run_and_measure(sampling_prog, - self.qubits, + classical_register, trials=samples) bitstring_tuples = list(map(tuple, bitstring_samples)) freq = Counter(bitstring_tuples) From 4e6549db1189646a5f77897c2ca592ac1c73592f Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:33:29 -0400 Subject: [PATCH 23/38] unembed_solution() added to ising_qaoa() --- grove/ising/ising_qaoa.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index fa54005..9933b3a 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -38,6 +38,23 @@ def energy_value(h, J, sol): return ener_ising +def unembed_solution(embedded_solution, inv_embedding): + """ + In case an embedding of logical to physical qubits was used. + Unembedding the solution string since QAOA returns solution string + that is sorted based on indices of physical qubits which might not + match up with qubit ordering in logical space. + + :param embedded_solution: (list) Solution string returned from QAOA. + :param inv_embedding: (dict) Dictionary that has physical qubits as keys + and logical qubits as values. + :return: Unembedded (correctly ordered) solution string. + :rtype: List. + """ + pos_list = [x[1] for x in sorted(inv_embedding.items(),key=lambda x: x[0])] + return [embedded_solution[i] for i,_ in sorted(enumerate(pos_list),key=lambda x: x[1])] + + def print_fun(x): print(x) From 9ea2e904760e953a4325ab4b8cc788ab231a4166 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:37:59 -0400 Subject: [PATCH 24/38] added ``embedding`` argument to ising_qaoa() accomodating for the new ``embedding`` argument in the QAOA class. the embedding removes the need for qubit_indices QAOA solution string must be unembedded since it will be ordered by indices in the logical space --- grove/ising/ising_qaoa.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index 9933b3a..cb79591 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -73,7 +73,7 @@ def ising_trans(x): return 1 -def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, +def ising_qaoa(h, J, num_steps=0, embedding=None, driver_operators=None, verbose=True, rand_seed=None, connection=None, samples=None, initial_beta=None, initial_gamma=None, minimizer_kwargs=None, vqe_option=None): @@ -84,6 +84,9 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, :param J: (dict) Interaction terms of the Ising problem (may be k-local). :param num_steps: (Optional.Default=2 * len(h)) Trotterization order for the QAOA algorithm. + :param embedding: (dict) (Optional. Default: Identity dict) Mapping of logical to physical + qubits in the QPU hardware graph. Logical qubits must be the + dict keys. :param driver_operators: (Optional. Default: X on all qubits.) The mixer/driver Hamiltonian used in QAOA. Can be used to enforce hard constraints and ensure that solution stays in feasible subspace. @@ -113,30 +116,35 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, if num_steps == 0: num_steps = 2 * len(h) - qubit_indices = set([ index for tuple_ in list(J.keys()) for index in tuple_] - + list(h.keys())) n_nodes = len(qubit_indices) + if embedding is None: + embedding = {i:i for i in range(n_nodes)} + inv_embedding = None + else: + # construct inverse embedding + inv_embedding = {embedding[k]:k for k in embedding.keys()} + cost_operators = [] driver_operators = [] for key in J.keys(): # first PauliTerm is multiplied with coefficient obtained from J - pauli_product = PauliTerm("Z", key[0], J[key]) + pauli_product = PauliTerm("Z", embedding[key[0]], J[key]) for i in range(1,len(key)): # multiply with additional Z PauliTerms depending # on the locality of the interaction terms - pauli_product *= PauliTerm("Z", key[i]) + pauli_product *= PauliTerm("Z", embedding[key[i]]) cost_operators.append(PauliSum([pauli_product])) for i in h.keys(): - cost_operators.append(PauliSum([PauliTerm("Z", i, h[i])])) + cost_operators.append(PauliSum([PauliTerm("Z", embedding[i], h[i])])) if driver_operators is None: driver_operators = [] # default to X mixer - for i in qubit_indices: + for i in embedding.values(): driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)])) if connection is None: @@ -156,6 +164,7 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, qaoa_inst = QAOA(connection, range(n_nodes), steps=num_steps, cost_ham=cost_operators, ref_hamiltonian=driver_operators, store_basis=True, rand_seed=rand_seed, + embedding=embedding, init_betas=initial_beta, init_gammas=initial_gamma, minimizer=minimize, @@ -164,6 +173,11 @@ def ising_qaoa(h, J, num_steps=0, driver_operators=None, verbose=True, betas, gammas = qaoa_inst.get_angles() most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas) + + if inv_embedding is not None: + # in case we use a custom embedding we need to reorder the solution + most_freq_string = unembed_solution(most_freq_string, inv_embedding) + most_freq_string_ising = [ising_trans(it) for it in most_freq_string] energy_ising = energy_value(h, J, most_freq_string_ising) param_prog = qaoa_inst.get_parameterized_program() From 701a83c627f6dec9e3c85e78455bbd7b8b5e4756 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 16:42:28 -0400 Subject: [PATCH 25/38] test for unembed_problem() added and bug fix for n_nodes --- grove/ising/ising_qaoa.py | 8 +++++--- grove/tests/ising/test_ising.py | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index cb79591..ec344e3 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -113,10 +113,12 @@ def ising_qaoa(h, J, num_steps=0, embedding=None, driver_operators=None, verbose :rtype: List, Integer or float, 'pyquil.quil.Program'. """ - if num_steps == 0: - num_steps = 2 * len(h) - n_nodes = len(qubit_indices) + n_nodes = len(set([ index for tuple_ in list(J.keys()) for index in tuple_] + + list(h.keys()))) + + if num_steps == 0: + num_steps = 2 * len(n_nodes) if embedding is None: embedding = {i:i for i in range(n_nodes)} diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index a9f36c8..b040715 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -25,6 +25,15 @@ def test_ising_trans(): ising_sol = [ising_trans(bit) for bit in sol] assert ising_sol == [1, -1, -1, 1] +def test_unembed_solution(): + sol = [0, 1, 1, 1] + inv_embedding = {20: 0, 13: 2, 23: 1, 15: 3} + assert unembed_solution(sol, inv_embedding) == [1, 1, 0, 1] + + sol = [0, 1, 1, 1, 0] + inv_embedding = {9: 0, 7: 2, 3: 1, 17: 3, 5: 4} + assert unembed_solution(sol, inv_embedding) == [1, 0, 1, 0, 1] + def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response From b7ea50d51917aa6604acc2c01996738562e1f8f4 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Wed, 28 Mar 2018 17:00:29 -0400 Subject: [PATCH 26/38] tests added for new ``embedding`` argument --- grove/tests/ising/test_ising.py | 19 ++++++++++++++++++- grove/tests/pyqaoa/test_qaoa.py | 11 +++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index b040715..10a6e87 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -117,7 +117,24 @@ def test_ising_mock(): J = {(0, 1, 2): 1.2, (0, 1, 2, 3): 2.5, (0, 2, 3): 0.5, (1, 3): 3.1} h = {0: -2.4, 1: 5.2, 3: -0.3} p = 1 - most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, driver_operators=swap_mixer, num_steps=p, vqe_option=None, connection=cxn) + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, driver_operators=swap_mixer, + num_steps=p, vqe_option=None, connection=cxn) assert most_freq_string_ising == [1, -1, -1, 1] assert energy_ising == -7.8 + + with patch("pyquil.api.QVMConnection") as cxn: + # Mock the response + cxn.run_and_measure.return_value = [[0, 1, 1, 0]] + cxn.expectation.return_value = [0, 0, 0, 0, 0, 0] # dummy + + embedding = {0: 15, 1: 3, 2: 17, 3: 7} + J = {(0, 1, 2): 1, (0, 1, 2, 3): 2 , (0, 2): 3} + h = {0: -2, 1: 5 , 3: -3} + p = 1 + most_freq_string_ising, energy_ising, circuit = ising_qaoa(h, J, embedding=embedding,num_steps=p, vqe_option=None, connection=cxn) + + # note that the solution string is different from the one define in the mock response! + # it was unembedded and resorted according to the indices in the logical space + assert most_freq_string_ising == [-1, 1, 1, -1] + assert energy_ising == 8 diff --git a/grove/tests/pyqaoa/test_qaoa.py b/grove/tests/pyqaoa/test_qaoa.py index b5c693b..f7bcf6e 100644 --- a/grove/tests/pyqaoa/test_qaoa.py +++ b/grove/tests/pyqaoa/test_qaoa.py @@ -77,6 +77,17 @@ def test_get_string(): assert len(freq) <= samples assert bitstring[0] == 1 + with patch('pyquil.api.QVMConnection') as cxn: + cxn.run_and_measure.return_value = [[0, 1] * 10] + qaoa = QAOA(qvm=cxn, n_qubits=2, embedding={0: 1, 1: 0}) + prog = Program() + prog.inst(X(0)) + qaoa.get_parameterized_program = lambda: lambda angles: prog + samples = 10 + bitstring, freq = qaoa.get_string(betas=None, gammas=None, samples=samples) + assert len(freq) <= samples + assert bitstring[0:2] == (0,1) + def test_ref_program_pass(): ref_prog = Program().inst([X(0), Y(1), Z(2)]) From e104b3398c694c87ce1654d16bc5e06de9adc5b1 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 19:36:38 -0400 Subject: [PATCH 27/38] added ``embedding`` argument to maxcut_qaoa --- grove/pyqaoa/maxcut_qaoa.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/grove/pyqaoa/maxcut_qaoa.py b/grove/pyqaoa/maxcut_qaoa.py index 1b82090..7108ec6 100644 --- a/grove/pyqaoa/maxcut_qaoa.py +++ b/grove/pyqaoa/maxcut_qaoa.py @@ -30,7 +30,7 @@ def print_fun(x): print(x) -def maxcut_qaoa(graph, steps=1, rand_seed=None, connection=None, samples=None, +def maxcut_qaoa(graph, steps=1, embedding=None, rand_seed=None, connection=None, samples=None, initial_beta=None, initial_gamma=None, minimizer_kwargs=None, vqe_option=None): """ @@ -38,6 +38,8 @@ def maxcut_qaoa(graph, steps=1, rand_seed=None, connection=None, samples=None, :param graph: Graph definition. Either networkx or list of tuples :param steps: (Optional. Default=1) Trotterization order for the QAOA algorithm. + :param embedding: (dict) Dictionary mapping logical qubits to physical + qubits on the Rigetti QPU. Logical qubits must be the dict keys. :param rand_seed: (Optional. Default=None) random seed when beta and gamma angles are not provided. :param connection: (Optional) connection to the QVM. Default is None. @@ -57,12 +59,18 @@ def maxcut_qaoa(graph, steps=1, rand_seed=None, connection=None, samples=None, maxcut_graph.add_edge(*edge) graph = maxcut_graph.copy() + n_qubits = len(graph.nodes()) + + if embedding is None: + # create identity dictionary + embedding = {i: i for i in range(n_qubits)} + cost_operators = [] driver_operators = [] for i, j in graph.edges(): - cost_operators.append(PauliTerm("Z", i, 0.5)*PauliTerm("Z", j) + PauliTerm("I", 0, -0.5)) + cost_operators.append(PauliTerm("Z", embedding[i], 0.5)*PauliTerm("Z", embedding[j]) + PauliTerm("I", min(embedding.values()), -0.5)) for i in graph.nodes(): - driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)])) + driver_operators.append(PauliSum([PauliTerm("X", embedding[i], -1.0)])) if connection is None: connection = CXN @@ -76,8 +84,10 @@ def maxcut_qaoa(graph, steps=1, rand_seed=None, connection=None, samples=None, 'samples': samples} qaoa_inst = QAOA(connection, list(graph.nodes()), steps=steps, cost_ham=cost_operators, + qaoa_inst = QAOA(connection, n_qubits, steps=steps, cost_ham=cost_operators, ref_hamiltonian=driver_operators, store_basis=True, rand_seed=rand_seed, + embedding=embedding, init_betas=initial_beta, init_gammas=initial_gamma, minimizer=minimize, From 49518e4709de40324ee43cdca07a7645e1d8bfdb Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 19:38:54 -0400 Subject: [PATCH 28/38] changing the way self.nstates and self.states are computed in order to get the correct wavefunction for analysis we need to get the correct number of states. if we shift qubits 0,1 to qubits 4,5 then we have 2**5 possible states in terms of the wavefunction (most of them will be uninteresting to us) --- grove/pyqaoa/qaoa.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index a9ffc49..9f52bbc 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -71,10 +71,6 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, self.qvm = qvm self.steps = steps self.qubits = qubits - self.nstates = 2 ** len(qubits) - if store_basis: - self.states = [np.binary_repr(i, width=len(self.qubits)) for i in range( - self.nstates)] self.betas = init_betas self.gammas = init_gammas self.vqe_options = vqe_options @@ -83,6 +79,10 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, else: # create identity dictionary self.embedding = {i: i for i in qubits} + self.nstates = 2 ** (max(self.embedding.values())+1) + if store_basis: + self.states = [np.binary_repr(i, width=self.n_qubits) for i in range( + self.nstates)] if driver_ref is not None: if not isinstance(driver_ref, pq.Program): From 5f13fa436732244a38fd413f5c83c35636669dda Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 19:41:34 -0400 Subject: [PATCH 29/38] fixing test by removing unneccessary gate operations in a seperate pull request to the pyquil repo we will propose to remove this sequence of gates that implement the identity operation. First of all, the operation is superfluous and second of all it messes up the wavefunction calculation since qubit 0 was hardcoded to always be used for this sequence of gates. It becomes a problem if we embedded our problem on qubit 3,4 and want to get the wavefunction since it will always try to involve qubit 0. --- grove/tests/pyqaoa/test_maxcut.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/grove/tests/pyqaoa/test_maxcut.py b/grove/tests/pyqaoa/test_maxcut.py index a037cd6..f43ebdc 100644 --- a/grove/tests/pyqaoa/test_maxcut.py +++ b/grove/tests/pyqaoa/test_maxcut.py @@ -89,11 +89,9 @@ def test_psiref_bar_p2(): # returns are the rotations correct? prog = param_prog([1.2, 3.4, 2.1, 4.5]) result_prog = Program().inst([H(0), H(1), - X(0), PHASE(1.05)(0), X(0), PHASE(1.05)(0), CNOT(0, 1), RZ(2.1)(1), CNOT(0, 1), H(0), RZ(-2.4)(0), H(0), H(1), RZ(-2.4)(1), H(1), - X(0), PHASE(2.25)(0), X(0), PHASE(2.25)(0), CNOT(0, 1), RZ(4.5)(1), CNOT(0, 1), H(0), RZ(-6.8)(0), H(0), H(1), RZ(-6.8)(1), H(1), From 8cf3d0eaccaf6eff34ce088a58e48280d325958f Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 23:27:28 -0400 Subject: [PATCH 30/38] moved the unembed_solution() function to QAOA doing it this way removes the need for code repetition in maxcut_qaoa, ising_qaoa and so on. this way we handle the unembedding logic in the get_string() method and make it much easier to use for the user. --- grove/ising/ising_qaoa.py | 9 --------- grove/pyqaoa/qaoa.py | 23 +++++++++++++++++++++-- grove/tests/pyqaoa/test_qaoa.py | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index ec344e3..e42226d 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -122,10 +122,6 @@ def ising_qaoa(h, J, num_steps=0, embedding=None, driver_operators=None, verbose if embedding is None: embedding = {i:i for i in range(n_nodes)} - inv_embedding = None - else: - # construct inverse embedding - inv_embedding = {embedding[k]:k for k in embedding.keys()} cost_operators = [] driver_operators = [] @@ -175,11 +171,6 @@ def ising_qaoa(h, J, num_steps=0, embedding=None, driver_operators=None, verbose betas, gammas = qaoa_inst.get_angles() most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas) - - if inv_embedding is not None: - # in case we use a custom embedding we need to reorder the solution - most_freq_string = unembed_solution(most_freq_string, inv_embedding) - most_freq_string_ising = [ising_trans(it) for it in most_freq_string] energy_ising = energy_value(h, J, most_freq_string_ising) param_prog = qaoa_inst.get_parameterized_program() diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index 9f52bbc..a7652e4 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -76,9 +76,11 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, self.vqe_options = vqe_options if embedding is not None: self.embedding = embedding + self.inv_embedding = {embedding[k]:k for k in embedding.keys()} else: # create identity dictionary self.embedding = {i: i for i in qubits} + self.inv_embedding = None self.nstates = 2 ** (max(self.embedding.values())+1) if store_basis: self.states = [np.binary_repr(i, width=self.n_qubits) for i in range( @@ -258,5 +260,22 @@ def get_string(self, betas, gammas, samples=100): trials=samples) bitstring_tuples = list(map(tuple, bitstring_samples)) freq = Counter(bitstring_tuples) - most_frequent_bit_string = max(freq, key=lambda x: freq[x]) - return most_frequent_bit_string, freq + most_freq_bit_string = max(freq, key=lambda x: freq[x]) + if self.inv_embedding is not None: + most_freq_bit_string = self.unembed_solution(most_freq_bit_string) + return most_freq_bit_string, freq + + + def unembed_solution(self, embedded_solution): + """ + In case an embedding of logical to physical qubits was used. + Unembedding the solution string since QAOA returns solution string + that is sorted based on indices of physical qubits which might not + match up with qubit ordering in logical space. + + :param embedded_solution: (list) Solution string returned from QAOA. + :return: Unembedded (correctly ordered) solution string. + :rtype: List. + """ + pos_list = [x[1] for x in sorted(self.inv_embedding.items(),key=lambda x: x[0])] + return tuple([embedded_solution[i] for i,_ in sorted(enumerate(pos_list),key=lambda x: x[1])]) diff --git a/grove/tests/pyqaoa/test_qaoa.py b/grove/tests/pyqaoa/test_qaoa.py index f7bcf6e..94a4bd8 100644 --- a/grove/tests/pyqaoa/test_qaoa.py +++ b/grove/tests/pyqaoa/test_qaoa.py @@ -86,7 +86,7 @@ def test_get_string(): samples = 10 bitstring, freq = qaoa.get_string(betas=None, gammas=None, samples=samples) assert len(freq) <= samples - assert bitstring[0:2] == (0,1) + assert bitstring[0:2] == (1,0) def test_ref_program_pass(): From e5610651de61987e4a39bb34393a48a0bdc83cfa Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 23:35:58 -0400 Subject: [PATCH 31/38] bug fix: unembedding before calculating freq --- grove/pyqaoa/qaoa.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index a7652e4..a3a7041 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -258,11 +258,12 @@ def get_string(self, betas, gammas, samples=100): bitstring_samples = self.qvm.run_and_measure(sampling_prog, classical_register, trials=samples) + if self.inv_embedding is not None: + for i, solution in enumerate(bitstring_samples): + bitstring_samples[i] = self.unembed_solution(bitstring_samples[i]) bitstring_tuples = list(map(tuple, bitstring_samples)) freq = Counter(bitstring_tuples) most_freq_bit_string = max(freq, key=lambda x: freq[x]) - if self.inv_embedding is not None: - most_freq_bit_string = self.unembed_solution(most_freq_bit_string) return most_freq_bit_string, freq From 179c24bcf59271bee0412ceba0d62396c4dca893 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 23:41:56 -0400 Subject: [PATCH 32/38] moved unembed_solution() from ising_qaoa() to QAOA class --- grove/ising/ising_qaoa.py | 17 ----------------- grove/tests/ising/test_ising.py | 9 --------- grove/tests/pyqaoa/test_qaoa.py | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/grove/ising/ising_qaoa.py b/grove/ising/ising_qaoa.py index e42226d..6b75795 100644 --- a/grove/ising/ising_qaoa.py +++ b/grove/ising/ising_qaoa.py @@ -38,23 +38,6 @@ def energy_value(h, J, sol): return ener_ising -def unembed_solution(embedded_solution, inv_embedding): - """ - In case an embedding of logical to physical qubits was used. - Unembedding the solution string since QAOA returns solution string - that is sorted based on indices of physical qubits which might not - match up with qubit ordering in logical space. - - :param embedded_solution: (list) Solution string returned from QAOA. - :param inv_embedding: (dict) Dictionary that has physical qubits as keys - and logical qubits as values. - :return: Unembedded (correctly ordered) solution string. - :rtype: List. - """ - pos_list = [x[1] for x in sorted(inv_embedding.items(),key=lambda x: x[0])] - return [embedded_solution[i] for i,_ in sorted(enumerate(pos_list),key=lambda x: x[1])] - - def print_fun(x): print(x) diff --git a/grove/tests/ising/test_ising.py b/grove/tests/ising/test_ising.py index 10a6e87..69b9d86 100644 --- a/grove/tests/ising/test_ising.py +++ b/grove/tests/ising/test_ising.py @@ -25,15 +25,6 @@ def test_ising_trans(): ising_sol = [ising_trans(bit) for bit in sol] assert ising_sol == [1, -1, -1, 1] -def test_unembed_solution(): - sol = [0, 1, 1, 1] - inv_embedding = {20: 0, 13: 2, 23: 1, 15: 3} - assert unembed_solution(sol, inv_embedding) == [1, 1, 0, 1] - - sol = [0, 1, 1, 1, 0] - inv_embedding = {9: 0, 7: 2, 3: 1, 17: 3, 5: 4} - assert unembed_solution(sol, inv_embedding) == [1, 0, 1, 0, 1] - def test_ising_mock(): with patch("pyquil.api.QVMConnection") as cxn: # Mock the response diff --git a/grove/tests/pyqaoa/test_qaoa.py b/grove/tests/pyqaoa/test_qaoa.py index 94a4bd8..adb5d79 100644 --- a/grove/tests/pyqaoa/test_qaoa.py +++ b/grove/tests/pyqaoa/test_qaoa.py @@ -88,6 +88,21 @@ def test_get_string(): assert len(freq) <= samples assert bitstring[0:2] == (1,0) +def test_unembed_solution(): + with patch('pyquil.api.QVMConnection') as cxn: + #cxn.run_and_measure.return_value = [[0, 1] * 10] + embedding = {0: 20, 2: 13, 1: 23, 3: 15} + qaoa = QAOA(qvm=cxn, n_qubits=2, embedding=embedding) + sol = [0, 1, 1, 1] + assert qaoa.unembed_solution(sol) == (1, 1, 0, 1) + + with patch('pyquil.api.QVMConnection') as cxn: + #cxn.run_and_measure.return_value = [[0, 1] * 10] + embedding = {0: 9, 2: 7, 1: 3, 3: 17, 4: 5} + qaoa = QAOA(qvm=cxn, n_qubits=2, embedding=embedding) + sol = [0, 1, 1, 1, 0] + assert qaoa.unembed_solution(sol) == (1, 0, 1, 0, 1) + def test_ref_program_pass(): ref_prog = Program().inst([X(0), Y(1), Z(2)]) From 199fa1b64e791586b3aaa67cf65fbb3453c63194 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Thu, 29 Mar 2018 23:46:02 -0400 Subject: [PATCH 33/38] added ``embedding`` to the numpart_qaoa --- grove/pyqaoa/numpartition_qaoa.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/grove/pyqaoa/numpartition_qaoa.py b/grove/pyqaoa/numpartition_qaoa.py index 6d722b4..8f1981c 100644 --- a/grove/pyqaoa/numpartition_qaoa.py +++ b/grove/pyqaoa/numpartition_qaoa.py @@ -22,12 +22,14 @@ CXN = api.QVMConnection() -def numpart_qaoa(asset_list, A=1.0, minimizer_kwargs=None, steps=1): +def numpart_qaoa(asset_list, A=1.0, embedding=None, minimizer_kwargs=None, steps=1): """ generate number partition driver and cost functions :param asset_list: list to binary parition :param A: (float) optional constant for level separation. Default=1. + :param embedding: (dict) Dictionary mapping logical qubits to physical + qubits on the Rigetti QPU. Logical qubits must be the dict keys. :param minimizer_kwargs: Arguments for the QAOA minimizer :param steps: (int) number of steps approximating the solution. """ @@ -46,9 +48,8 @@ def numpart_qaoa(asset_list, A=1.0, minimizer_kwargs=None, steps=1): 'options': {'ftol': 1.0e-2, 'xtol': 1.0e-2, 'disp': True}} - qaoa_inst = QAOA(CXN, len(asset_list), steps=steps, cost_ham=cost_operators, - ref_hamiltonian=ref_operators, store_basis=True, + ref_hamiltonian=ref_operators, embedding=embedding, store_basis=True, minimizer=minimize, minimizer_kwargs=minimizer_kwargs, vqe_options={'disp': True}) From 065eeb390b1866b161ee472027a6129b82ae07d7 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Fri, 30 Mar 2018 00:04:32 -0400 Subject: [PATCH 34/38] added documentation for the new ``embedding`` argument --- docs/qaoa.rst | 92 +++++++++++++++++++++++++++++++++ grove/tests/pyqaoa/test_qaoa.py | 2 - 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/docs/qaoa.rst b/docs/qaoa.rst index 9d8d25f..a9a09ff 100644 --- a/docs/qaoa.rst +++ b/docs/qaoa.rst @@ -23,6 +23,7 @@ Cost Functions * ``numpartition_qaoa.py`` implements the cost function for bipartitioning a list of numbers. ++.. _quickstart-example: Quickstart Examples ------------------- @@ -408,6 +409,97 @@ As expected the approximate evolution becomes more accurate as the number of steps (\\(\\alpha\\)) is increased. For this simple model \\(\\alpha = 2\\) is sufficient to find the two degnerate cuts of the four ring graph. +QPU Embedding +~~~~~~~~~~~~~ + +If you want to use QAOA with the QPU you most probably want full control +over which qubits are being used. First of all, there might be dead qubits +on the chip that no one should use. Secondly, your QAOA implementation might +only require five qubits to run in which case you should try to select a subset +of qubits that is in close proximity on the chip (to decrease the number of +topological SWAPs) and which has the best specs. You can manually select the +physical qubits by using the ``embedding`` argument when instantiating your +QAOA instance. In this example, we will solve the same square ring Max-CUT +problem as in the :ref:`quickstart-example`. To solve this problem, we require +four qubits. Let's have a look at the hardware graph of the 19Q-Acorn chip: + +.. image:: qaoa/acorn_hardware_graph.png + :align: center + :scale: 75% + +We certainly don't want to use qubits 3 and 15 which are currently turned off. After looking at +the decoherence times in the `PyQuil documentation `_ +we decide to use the qubits \\(0, 6, 1\\) and \\(7\\) (for this example we want to keep the indices +small). Next to the square ring graph we can now define the ``embedding``: + +.. code-block:: python + + import numpy as np + from grove.pyqaoa.maxcut_qaoa import maxcut_qaoa + import pyquil.api as api + + qvm_connection = api.QVMConnection() + + square_ring = [(0,1),(1,2),(2,3),(3,0)] + embedding = {0: 0, 1: 6, 2: 7, 3: 1} + +We instantiate the algorithm, this time with the optional ``embedding`` argument and run +the optimization routine on our QVM: + +.. code-block:: python + + steps = 2 +inst = maxcut_qaoa(graph=square_ring, steps=steps, embedding=embedding) + betas, gammas = inst.get_angles() + +And let's visualize the wavefunction again: + +.. code-block:: python + + t = np.hstack((betas, gammas)) + param_prog = inst.get_parameterized_program() + prog = param_prog(t) + wf = qvm_connection.wavefunction(prog) + wf = wf.amplitudes + + for state_index in range(2**4): + print(inst.states[state_index], np.conj(wf[state_index])*wf[state_index]) + +You should then see that the algorithm converges on the solutions of \\(0110\\) and \\(1001\\)! :: + + 0000 (5.708663566132753e-10+0j) + 0001 (7.764079130797125e-06+0j) + 0010 (7.764079130796849e-06+0j) + 0011 (1.432693478156927e-06+0j) + 0100 (7.764079130797427e-06+0j) + 0101 (1.4326934781570087e-06+0j) + 0110 (0.4999660777256528+0j) + 0111 (7.764079130797218e-06+0j) + 1000 (7.764079130797218e-06+0j) + 1001 (0.4999660777256528+0j) + 1010 (1.4326934781570087e-06+0j) + 1011 (7.764079130797427e-06+0j) + 1100 (1.432693478156927e-06+0j) + 1101 (7.764079130796849e-06+0j) + 1110 (7.764079130797125e-06+0j) + 1111 (5.708663566132753e-10+0j) + +If you compare this with the solution in the :ref:`quickstart-example` you will realize +that the solutions don't match up. The reason for this is that we have to *unembed* our solution. +The QPU/QVM returns the bits ordered in reverse order depending on the indices of the physical qubits +(the values in the ``embedding`` dict). Hence, the solution 0110 string must be read as \\(q_{7}q_{6}q_{1}q_{0}\\). +Unembedding means applying the transforms \\(q_{7}\\rightarrow q_{2}, q_{6} \\rightarrow q_{1}, +q_{1} \\rightarrow q_{3}, q_{0}\\rightarrow q_{0} \\). The order of the returned solution string is +then \\(q_{2}q_{1}q_{3}q_{0}\\) which is obviously not correctly ordered. Thus, it remains to correctly +order the bits by reverse indices such that we get \\(q_{3}q_{2}q_{1}q_{0}\\). The first unembedded solution +\\(0110\\) then reads \\(0101\\) and the other unembedded solution \\(1001\\) becomes \\(1010\\) which are +the same solutions that we obtained in the :ref:`quickstart-example`! + +Conveniently, if you use the ``get_string`` method on the QAOA instance the unembedding happens without you +even having to worry about it! You pass the embedding when instantiating the QAOA instance and you will never +ever have to think in terms of the physical qubit indices but the solution string will be ordered in reverse +order of your logical qubits! + Source Code Docs ---------------- diff --git a/grove/tests/pyqaoa/test_qaoa.py b/grove/tests/pyqaoa/test_qaoa.py index adb5d79..fd788e4 100644 --- a/grove/tests/pyqaoa/test_qaoa.py +++ b/grove/tests/pyqaoa/test_qaoa.py @@ -90,14 +90,12 @@ def test_get_string(): def test_unembed_solution(): with patch('pyquil.api.QVMConnection') as cxn: - #cxn.run_and_measure.return_value = [[0, 1] * 10] embedding = {0: 20, 2: 13, 1: 23, 3: 15} qaoa = QAOA(qvm=cxn, n_qubits=2, embedding=embedding) sol = [0, 1, 1, 1] assert qaoa.unembed_solution(sol) == (1, 1, 0, 1) with patch('pyquil.api.QVMConnection') as cxn: - #cxn.run_and_measure.return_value = [[0, 1] * 10] embedding = {0: 9, 2: 7, 1: 3, 3: 17, 4: 5} qaoa = QAOA(qvm=cxn, n_qubits=2, embedding=embedding) sol = [0, 1, 1, 1, 0] From a27ea6ac538ba1bdca4908c0d20cafd6ef577df2 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Fri, 30 Mar 2018 00:11:46 -0400 Subject: [PATCH 35/38] fixed some minor errors in the qaoa read the docs --- docs/qaoa.rst | 79 +++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/docs/qaoa.rst b/docs/qaoa.rst index a9a09ff..9dfce8a 100644 --- a/docs/qaoa.rst +++ b/docs/qaoa.rst @@ -23,7 +23,7 @@ Cost Functions * ``numpartition_qaoa.py`` implements the cost function for bipartitioning a list of numbers. -+.. _quickstart-example: +.. _quickstart-example: Quickstart Examples ------------------- @@ -106,7 +106,7 @@ algorithm for finding "a 'good' solution to an optimization problem" [`1 `_, `2 `_]. What's with the name? For a given NP-Hard problem an approximate algorithm is a -polynomial-time algorithm that solves every instance of the problem with some +polynomial-time algorithm that solves every instance of the problem with some guaranteed quality in expectation. The value of merit is the ratio between the quality of the polynomial time solution and the quality of the true solution. @@ -155,8 +155,8 @@ barbell graph there are two equal weight partitionings that correspond to a maximum cut (the right two partitonings)--i.e. cutting the barbell in half. One can denote which set \\( S \\) or \\( \\overline{S} -\\) a node is in with either a \\(0\\) or a \\(1\\), respectively, in a bit -string of length \\( N \\). The four partitionings of the barbell graph listed +\\) a node is in with either a \\(0\\) or a \\(1\\), respectively, in a bit +string of length \\( N \\). The four partitionings of the barbell graph listed above are, \\(\\{ 00, 11, 01, 10 \\} \\)---where the left most bit is node \\(A\\) and the right most bit is node \\(B\\). The bit string representation makes it easy to represent a particular partition of the graph. Each bit @@ -166,7 +166,7 @@ For any graph, the bit string representations of the node partitionings are alwa length \\(N\\). The total number of partitionings grows as \\(2^{N}\\). For example, a square ring graph -.. image:: qaoa/square.png +.. image:: qaoa/square.png :scale: 55% :align: center @@ -194,9 +194,9 @@ particular quality. For example, a famous polynomial time algorithm is the randomized partitioning approach. One simply iterates over the nodes of the graph and flips a coin. If the coin is heads the node is in \\( S \\), if tails the node is in \\( \\overline{S} \\). The quality of the random -assignment algorithm is at least 50 percent of the maximum cut. -For a coin-flip process the probability of an edge being in the cut is 50\%. -Therefore, the expectation value of a cut produced by random assignment can be +assignment algorithm is at least 50 percent of the maximum cut. +For a coin-flip process the probability of an edge being in the cut is 50\%. +Therefore, the expectation value of a cut produced by random assignment can be written as follows: $$\\sum_{e \\in E} w_{e} \\cdot \\mathrm{Pr}(e \\in \\mathrm{cut}) = \\frac{1}{2} \\sum_{e \\in E}w_{e}$$ @@ -211,7 +211,7 @@ give cuts of expected value at least 0.87856 times the maximum cut [`3 Quantum Approximate Optimization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -One can think of the bit strings (or set of bit strings) that correspond to the +One can think of the bit strings (or set of bit strings) that correspond to the maximum cut on a graph as the ground state of a Hamiltonian encoding the cost function. The form of this Hamiltonian can be determined by constructing the classical function that returns a 1 (or the weight of the edge) if the edge spans two-nodes in different sets, or 0 if the nodes are in the same set. @@ -220,13 +220,13 @@ C_{ij} = \\frac{1}{2}(1 - z_{i}z_{j}) \\end{align} \\( z_{i}\\) or \\(z_{j}\\) is \\(+1\\) if node \\(i\\) or node \\(j\\) is in \\(S\\) or \\(-1\\) if node \\(i\\) or node \\(j\\) is in \\(\\overline{S}\\). The total cost is the -sum of all \\( (i ,j) \\) node pairs that form the edge set of the graph. -This suggests that for MAX-CUT the Hamiltonian that encodes the problem is +sum of all \\( (i ,j) \\) node pairs that form the edge set of the graph. +This suggests that for MAX-CUT the Hamiltonian that encodes the problem is $$\\sum_{ij}\\frac{1}{2}(\\mathbf{I} - \\sigma_{i}^{z}\\sigma_{j}^{z})$$ where the sum is over \\( (i,j) \\) node pairs that form the edges of the graph. -The quantum-approximate-optimization-algorithm relies on the fact that we can +The quantum-approximate-optimization-algorithm relies on the fact that we can prepare something approximating the ground state of this Hamiltonian and -perform a measurement on that state. Performing a measurement on the \\(N\\)-body +perform a measurement on that state. Performing a measurement on the \\(N\\)-body quantum state returns the bit string corresponding to the maximum cut with high probability. @@ -253,9 +253,9 @@ QAOA identifies the ground state of the MAXCUT Hamiltonian by evolving from a reference state. This reference state is the ground state of a Hamiltonian that couples all \\( 2^{N} \\) states that form the basis of the cost Hamiltonian---i.e. the diagonal basis for cost function. -For MAX-CUT this is the \\(Z\\) computational basis. +For MAX-CUT this is the \\(Z\\) computational basis. -The evolution between the ground state of the reference Hamiltonian +The evolution between the ground state of the reference Hamiltonian and the ground state of the MAXCUT Hamiltonian can be generated by an interpolation between the two operators \\begin{align} @@ -293,8 +293,8 @@ eigenvectors of the \\(\\sigma_{x}\\) operator (\\(\\mid + \\end{align} The reference state is easily generated by performing a Hadamard gate on each -qubit--assuming the initial state of the system is all zeros. The Quil code -generating this state is +qubit--assuming the initial state of the system is all zeros. The Quil code +generating this state is .. code-block:: c @@ -307,7 +307,7 @@ pyQAOA requires the user to input how many slices (approximate steps) for the evolution between the reference and MAXCUT Hamiltonian. The algorithm then variationally determines the parameters for the rotations (denoted \\(\\beta\\) and -\\(\\gamma\\)) +\\(\\gamma\\)) using the quantum-variational-eigensolver method [`4 `_][`5 `_] that maximizes the cost function. @@ -326,11 +326,11 @@ where \\begin{align} U(\\hat{H}_{\\mathrm{ref}}, \\beta_{i}) = e^{-i \\hat{H}_{\\mathrm{ref}} \\beta_{i}} \\end{align} -and +and \\begin{align} U(\\hat{H}_{\\mathrm{MAXCUT}}, \\gamma_{i}) = e^{-i \\hat{H}_{\\mathrm{MAXCUT}} \\gamma_{i}} \\end{align} -\\( U(\\hat{H}_{\\mathrm{ref}}, \\beta_{i}) \\) and \\( U(\\hat{H}_{\\mathrm{MAXCUT}}, \\gamma_{i})\\) can be expressed as a short quantum circuit. +\\( U(\\hat{H}_{\\mathrm{ref}}, \\beta_{i}) \\) and \\( U(\\hat{H}_{\\mathrm{MAXCUT}}, \\gamma_{i})\\) can be expressed as a short quantum circuit. For the \\(U(\\hat{H}_{\\mathrm{ref}}, \\beta_{i})\\) term (or mixing term) all operators in the sum commute and thus can be split into a product of @@ -348,7 +348,7 @@ e^{-i\\hat{H}_{\\mathrm{ref}} \\beta_{i}} = \\prod_{n = H 1 RZ(beta_i) 1 H 1 - + Of course, if RX is in the natural gate set for the quantum-processor this Quil is compiled into a set of RX rotations. The Quil code for the cost function @@ -360,21 +360,21 @@ looks like this: .. code-block:: c X 0 - PHASE(gamma{i}/2) 0 + PHASE(gamma{i}/2) 0 X 0 PHASE(gamma{i}/2) 0 CNOT 0 1 RZ(gamma{i}) 1 CNOT 0 1 -Executing the Quil code will generate the +Executing the Quil code will generate the \\( \\mid + \\rangle_{1}\\otimes\\mid + \\rangle_{0}\\) state and perform the evolution with selected \\(\\beta\\) and \\(\\gamma\\) angles. \\begin{align} \\mid \\beta, \\gamma \\rangle = e^{-i \\hat{H}_{\\mathrm{ref}} \\beta_{1}}e^{-i \\hat{H}_{\\mathrm{MAXCUT}} \\gamma_{1}}e^{-i \\hat{H}_{\\mathrm{ref}} \\beta_{0}}e^{-i \\hat{H}_{\\mathrm{MAXCUT}} \\gamma_{0}} \\mid + \\rangle_{N-1,...,0} \\end{align} In order to indentify the set of \\(\\beta\\) and \\(\\gamma\\) angles that -maximize the objective function +maximize the objective function \\begin{align} \\mathrm{Cost} = \\langle \\beta, \\gamma \\mid \\hat{H}_{\\mathrm{MAXCUT}} \\mid \\beta, \\gamma \\rangle @@ -384,10 +384,10 @@ pyQAOA leverages the classical-quantum hybrid approach known as the quantum-variational-eigensolver[`4 `_][`5 `_]. The quantum processor is used to prepare a state through a polynomial number of operations which is -then used to evaluate the cost. Evaluating the cost +then used to evaluate the cost. Evaluating the cost (\\( \\langle \\beta, \\gamma \\mid \\hat{H}_{\\mathrm{MAXCUT}} \\mid \\beta, \\gamma \\rangle\\)) requires -many preparations and measurements to generate enough samples to accurately -construct the distribution. The classical computer then generates a new set of +many preparations and measurements to generate enough samples to accurately +construct the distribution. The classical computer then generates a new set of parameters (\\( \\beta, \\gamma\\)) for maximizing the cost function. .. image:: qaoa/VQE.png @@ -405,7 +405,7 @@ state with \\(\\beta, \\gamma\\) and sampling. :scale: 55% The probability distributions above are for the four ring graph discussed earlier. -As expected the approximate evolution becomes more accurate as the number of +As expected the approximate evolution becomes more accurate as the number of steps (\\(\\alpha\\)) is increased. For this simple model \\(\\alpha = 2\\) is sufficient to find the two degnerate cuts of the four ring graph. @@ -449,7 +449,7 @@ the optimization routine on our QVM: .. code-block:: python steps = 2 -inst = maxcut_qaoa(graph=square_ring, steps=steps, embedding=embedding) + inst = maxcut_qaoa(graph=square_ring, steps=steps, embedding=embedding) betas, gammas = inst.get_angles() And let's visualize the wavefunction again: @@ -484,21 +484,20 @@ You should then see that the algorithm converges on the solutions of \\(0110\\) 1110 (7.764079130797125e-06+0j) 1111 (5.708663566132753e-10+0j) -If you compare this with the solution in the :ref:`quickstart-example` you will realize -that the solutions don't match up. The reason for this is that we have to *unembed* our solution. -The QPU/QVM returns the bits ordered in reverse order depending on the indices of the physical qubits -(the values in the ``embedding`` dict). Hence, the solution 0110 string must be read as \\(q_{7}q_{6}q_{1}q_{0}\\). -Unembedding means applying the transforms \\(q_{7}\\rightarrow q_{2}, q_{6} \\rightarrow q_{1}, -q_{1} \\rightarrow q_{3}, q_{0}\\rightarrow q_{0} \\). The order of the returned solution string is -then \\(q_{2}q_{1}q_{3}q_{0}\\) which is obviously not correctly ordered. Thus, it remains to correctly -order the bits by reverse indices such that we get \\(q_{3}q_{2}q_{1}q_{0}\\). The first unembedded solution -\\(0110\\) then reads \\(0101\\) and the other unembedded solution \\(1001\\) becomes \\(1010\\) which are +If you compare this with the solution in the :ref:`quickstart-example` you will realize that the solutions +don't match up. The reason for this is that we have to *unembed* our solution. The QPU/QVM returns the bits +ordered in reverse order depending on the indices of the physical qubits (the values in the ``embedding`` dict). +Hence, the solution 0110 string must be read as \\(q_{7}q_{6}q_{1}q_{0}\\). Unembedding means applying the +transforms \\(q_{7}\\rightarrow q_{2}, q_{6} \\rightarrow q_{1}, q_{1} \\rightarrow q_{3}, q_{0}\\rightarrow q_{0} \\). +The order of the returned solution string is then \\(q_{2}q_{1}q_{3}q_{0}\\) which is obviously not correctly ordered. +Thus, it remains to correctly order the bits by reverse indices such that we get \\(q_{3}q_{2}q_{1}q_{0}\\). The first +unembedded solution \\(0110\\) then reads \\(0101\\) and the other unembedded solution \\(1001\\) becomes \\(1010\\) which are the same solutions that we obtained in the :ref:`quickstart-example`! Conveniently, if you use the ``get_string`` method on the QAOA instance the unembedding happens without you even having to worry about it! You pass the embedding when instantiating the QAOA instance and you will never -ever have to think in terms of the physical qubit indices but the solution string will be ordered in reverse -order of your logical qubits! +ever have to think in terms of the physical qubit indices since the solution string will be ordered in reverse +order of your logical qubits! Everything is as if you've never did an embedding. Source Code Docs ---------------- From 1aa24d7fb37ea510523586dd36be255a4f05c92a Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Fri, 30 Mar 2018 00:32:47 -0400 Subject: [PATCH 36/38] ensured compatibility with most recent master branch included support for argument change from n_qubits to qubits (list) --- grove/pyqaoa/maxcut_qaoa.py | 1 - grove/pyqaoa/qaoa.py | 2 +- grove/tests/pyqaoa/test_qaoa.py | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/grove/pyqaoa/maxcut_qaoa.py b/grove/pyqaoa/maxcut_qaoa.py index 7108ec6..8fd5699 100644 --- a/grove/pyqaoa/maxcut_qaoa.py +++ b/grove/pyqaoa/maxcut_qaoa.py @@ -84,7 +84,6 @@ def maxcut_qaoa(graph, steps=1, embedding=None, rand_seed=None, connection=None, 'samples': samples} qaoa_inst = QAOA(connection, list(graph.nodes()), steps=steps, cost_ham=cost_operators, - qaoa_inst = QAOA(connection, n_qubits, steps=steps, cost_ham=cost_operators, ref_hamiltonian=driver_operators, store_basis=True, rand_seed=rand_seed, embedding=embedding, diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index a3a7041..e8b509b 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -83,7 +83,7 @@ def __init__(self, qvm, qubits, steps=1, init_betas=None, self.inv_embedding = None self.nstates = 2 ** (max(self.embedding.values())+1) if store_basis: - self.states = [np.binary_repr(i, width=self.n_qubits) for i in range( + self.states = [np.binary_repr(i, width=len(self.qubits)) for i in range( self.nstates)] if driver_ref is not None: diff --git a/grove/tests/pyqaoa/test_qaoa.py b/grove/tests/pyqaoa/test_qaoa.py index fd788e4..b0d3be0 100644 --- a/grove/tests/pyqaoa/test_qaoa.py +++ b/grove/tests/pyqaoa/test_qaoa.py @@ -79,7 +79,7 @@ def test_get_string(): with patch('pyquil.api.QVMConnection') as cxn: cxn.run_and_measure.return_value = [[0, 1] * 10] - qaoa = QAOA(qvm=cxn, n_qubits=2, embedding={0: 1, 1: 0}) + qaoa = QAOA(qvm=cxn, qubits=[0, 1], embedding={0: 1, 1: 0}) prog = Program() prog.inst(X(0)) qaoa.get_parameterized_program = lambda: lambda angles: prog @@ -90,14 +90,14 @@ def test_get_string(): def test_unembed_solution(): with patch('pyquil.api.QVMConnection') as cxn: - embedding = {0: 20, 2: 13, 1: 23, 3: 15} - qaoa = QAOA(qvm=cxn, n_qubits=2, embedding=embedding) + embedding = {0: 20, 1: 23, 2: 13, 3: 15} + qaoa = QAOA(qvm=cxn, qubits=[0, 1, 2, 3], embedding=embedding) sol = [0, 1, 1, 1] assert qaoa.unembed_solution(sol) == (1, 1, 0, 1) with patch('pyquil.api.QVMConnection') as cxn: - embedding = {0: 9, 2: 7, 1: 3, 3: 17, 4: 5} - qaoa = QAOA(qvm=cxn, n_qubits=2, embedding=embedding) + embedding = {0: 9, 1: 3, 2: 7, 3: 17, 4: 5} + qaoa = QAOA(qvm=cxn, qubits=[0, 1, 2, 3, 4], embedding=embedding) sol = [0, 1, 1, 1, 0] assert qaoa.unembed_solution(sol) == (1, 0, 1, 0, 1) From d8d92ceb88806cf9ded30aaa75a8b76fa01549c6 Mon Sep 17 00:00:00 2001 From: Mark Fingerhuth Date: Fri, 30 Mar 2018 00:43:49 -0400 Subject: [PATCH 37/38] added the 19Q-acorn hardware graph image --- docs/qaoa/acorn_hardware_graph.png | Bin 0 -> 41775 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/qaoa/acorn_hardware_graph.png diff --git a/docs/qaoa/acorn_hardware_graph.png b/docs/qaoa/acorn_hardware_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb6c0341a11e8caef141780a6d53c1a91ffe7aa GIT binary patch literal 41775 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�I4*Z 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 literal 0 HcmV?d00001 From aea84bb47b8620be72f1291ae77e1883c0d100f0 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Tue, 10 Apr 2018 14:30:51 -0400 Subject: [PATCH 38/38] qaoa: Do not measure twice Adding measurement instructions and then running with measure_and_run makes all trials sample the same state. --- grove/pyqaoa/qaoa.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/grove/pyqaoa/qaoa.py b/grove/pyqaoa/qaoa.py index e8b509b..6e3f741 100644 --- a/grove/pyqaoa/qaoa.py +++ b/grove/pyqaoa/qaoa.py @@ -251,8 +251,6 @@ def get_string(self, betas, gammas, samples=100): param_prog = self.get_parameterized_program() stacked_params = np.hstack((betas, gammas)) sampling_prog = param_prog(stacked_params) - for i in self.embedding.values(): - sampling_prog.measure(i, [i]) classical_register = list(sorted(self.embedding.values())) bitstring_samples = self.qvm.run_and_measure(sampling_prog,