From e7a513ad0414e87d3afb432882de8e9187e73d13 Mon Sep 17 00:00:00 2001 From: Dan Lykov Date: Fri, 8 Sep 2023 15:58:02 -0400 Subject: [PATCH 1/6] fix precomputation in get_objective_maxcut.py --- qokit/maxcut.py | 26 ++++++++++++++++++++++++++ qokit/qaoa_objective_maxcut.py | 6 ++++-- tests/test_maxcut.py | 15 ++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/qokit/maxcut.py b/qokit/maxcut.py index 433f00e68..a60b71d88 100644 --- a/qokit/maxcut.py +++ b/qokit/maxcut.py @@ -5,6 +5,7 @@ """ Helper functions for the Maximum Cut (MaxCut) problem """ +from qokit.fur.qaoa_simulator_base import TermsType import numpy as np import networkx as nx @@ -20,6 +21,31 @@ def maxcut_obj(x: np.ndarray, w: np.ndarray) -> float: X = np.outer(x, (1 - x)) return np.sum(w * X) # type: ignore +def get_maxcut_terms(G: nx.Graph) -> TermsType: + """Get terms corresponding to cost function value + + .. math:: + + S = \\sum_{(i,j,w)\\in G} w*(1-s_i*s_j)/2 + + Args: + G: MaxCut problem graph + Returns: + terms to be used in the simulation + """ + if nx.is_weighted(G): + terms = [( + -float(G[u][v]["weight"])/2, + (int(u), int(v)) + ) for u, v, *_ in G.edges()] + total_w = sum([float(G[u][v]["weight"]) for u, v, *_ in G.edges()]) + + else: + terms = [(-1/2, (int(e[0]), int(e[1]))) for e in G.edges()] + total_w = int(G.number_of_edges()) + N = G.number_of_nodes() + terms.append((+total_w/2, tuple())) + return terms def get_adjacency_matrix(G: nx.Graph) -> np.ndarray: """Get adjacency matrix to be used in maxcut_obj diff --git a/qokit/qaoa_objective_maxcut.py b/qokit/qaoa_objective_maxcut.py index 05756959e..cd81308bc 100644 --- a/qokit/qaoa_objective_maxcut.py +++ b/qokit/qaoa_objective_maxcut.py @@ -8,7 +8,7 @@ import warnings from .utils import precompute_energies -from .maxcut import maxcut_obj, get_adjacency_matrix +from .maxcut import maxcut_obj, get_adjacency_matrix, get_maxcut_terms from .qaoa_circuit_maxcut import get_parameterized_qaoa_circuit from .qaoa_objective import get_qaoa_objective @@ -53,13 +53,14 @@ def get_qaoa_maxcut_objective( f : callable Function returning the negative of expected value of QAOA with parameters theta """ + terms = None if precomputed_cuts is not None and G is not None: warnings.warn("If precomputed_cuts is passed, G is ignored") if precomputed_cuts is None: assert G is not None, "G must be passed if precomputed_cuts is None" - precomputed_cuts = precompute_energies(maxcut_obj, N, w=get_adjacency_matrix(G)) + terms = get_maxcut_terms(G) if simulator == "qiskit": assert G is not None, "G must be passed if simulator == 'qiskit'" @@ -73,6 +74,7 @@ def get_qaoa_maxcut_objective( p=p, precomputed_diagonal_hamiltonian=precomputed_cuts, precomputed_objectives=precomputed_cuts, + terms=terms, precomputed_optimal_bitstrings=precomputed_optimal_bitstrings, parameterized_circuit=parameterized_circuit, parameterization=parameterization, diff --git a/tests/test_maxcut.py b/tests/test_maxcut.py index 8b3e1152b..983f4d437 100644 --- a/tests/test_maxcut.py +++ b/tests/test_maxcut.py @@ -9,13 +9,14 @@ from functools import partial from qiskit.providers.aer import AerSimulator -from qokit.maxcut import maxcut_obj, get_adjacency_matrix +from qokit.maxcut import maxcut_obj, get_adjacency_matrix, get_maxcut_terms from qokit.qaoa_objective_maxcut import get_qaoa_maxcut_objective from qokit.qaoa_circuit_maxcut import get_qaoa_circuit, get_parameterized_qaoa_circuit from qokit.utils import brute_force, precompute_energies from qokit.parameter_utils import get_sk_gamma_beta, get_fixed_gamma_beta +import qokit test_maxcut_folder = Path(__file__).parent @@ -79,6 +80,18 @@ def test_maxcut_weighted_qaoa_obj(): assert np.allclose(sv, sv_param) assert np.isclose(precomputed_cuts.dot(np.abs(sv) ** 2), row["Expected cut of QAOA"]) +def test_maxcut_precompute(): + N = 4 + G = nx.random_regular_graph(3, N) + print(G.edges()) + for (u,v,w) in G.edges(data=True): + w['weight'] = np.random.rand() + precomputed_cuts = precompute_energies(maxcut_obj, N, w=get_adjacency_matrix(G)) + simclass = qokit.fur.choose_simulator('gpu') + terms = get_maxcut_terms(G) + sim = simclass(N, terms=terms) + cuts = sim.get_cost_diagonal() + assert np.allclose(precomputed_cuts, cuts, atol=1e-7) def test_sk_ini_maxcut(): N = 10 From d340c201f0ba7bcf50563a271f2d3d1d34db84a8 Mon Sep 17 00:00:00 2001 From: Dan Lykov Date: Fri, 8 Sep 2023 17:55:44 -0400 Subject: [PATCH 2/6] black fix --- qokit/maxcut.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qokit/maxcut.py b/qokit/maxcut.py index a60b71d88..d1c6e7ff4 100644 --- a/qokit/maxcut.py +++ b/qokit/maxcut.py @@ -21,6 +21,7 @@ def maxcut_obj(x: np.ndarray, w: np.ndarray) -> float: X = np.outer(x, (1 - x)) return np.sum(w * X) # type: ignore + def get_maxcut_terms(G: nx.Graph) -> TermsType: """Get terms corresponding to cost function value @@ -34,19 +35,17 @@ def get_maxcut_terms(G: nx.Graph) -> TermsType: terms to be used in the simulation """ if nx.is_weighted(G): - terms = [( - -float(G[u][v]["weight"])/2, - (int(u), int(v)) - ) for u, v, *_ in G.edges()] + terms = [(-float(G[u][v]["weight"]) / 2, (int(u), int(v))) for u, v, *_ in G.edges()] total_w = sum([float(G[u][v]["weight"]) for u, v, *_ in G.edges()]) else: - terms = [(-1/2, (int(e[0]), int(e[1]))) for e in G.edges()] + terms = [(-1 / 2, (int(e[0]), int(e[1]))) for e in G.edges()] total_w = int(G.number_of_edges()) N = G.number_of_nodes() - terms.append((+total_w/2, tuple())) + terms.append((+total_w / 2, tuple())) return terms + def get_adjacency_matrix(G: nx.Graph) -> np.ndarray: """Get adjacency matrix to be used in maxcut_obj Args: From 46968635dcd1847ea3e846b6af522300b948e21f Mon Sep 17 00:00:00 2001 From: Dan Lykov Date: Fri, 8 Sep 2023 18:08:00 -0400 Subject: [PATCH 3/6] black fix --- tests/test_maxcut.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_maxcut.py b/tests/test_maxcut.py index 983f4d437..1f6a2db84 100644 --- a/tests/test_maxcut.py +++ b/tests/test_maxcut.py @@ -80,19 +80,21 @@ def test_maxcut_weighted_qaoa_obj(): assert np.allclose(sv, sv_param) assert np.isclose(precomputed_cuts.dot(np.abs(sv) ** 2), row["Expected cut of QAOA"]) + def test_maxcut_precompute(): N = 4 G = nx.random_regular_graph(3, N) print(G.edges()) - for (u,v,w) in G.edges(data=True): - w['weight'] = np.random.rand() + for u, v, w in G.edges(data=True): + w["weight"] = np.random.rand() precomputed_cuts = precompute_energies(maxcut_obj, N, w=get_adjacency_matrix(G)) - simclass = qokit.fur.choose_simulator('gpu') + simclass = qokit.fur.choose_simulator("gpu") terms = get_maxcut_terms(G) sim = simclass(N, terms=terms) cuts = sim.get_cost_diagonal() assert np.allclose(precomputed_cuts, cuts, atol=1e-7) + def test_sk_ini_maxcut(): N = 10 for d, max_p in [(3, 5), (5, 5)]: From 5861b68b007fd5b967386b2a7602819b24649060 Mon Sep 17 00:00:00 2001 From: Dan Lykov Date: Sat, 9 Sep 2023 12:19:42 -0400 Subject: [PATCH 4/6] use only available simulators for maxcut objective test --- tests/test_maxcut.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_maxcut.py b/tests/test_maxcut.py index 1f6a2db84..60e5469f1 100644 --- a/tests/test_maxcut.py +++ b/tests/test_maxcut.py @@ -8,6 +8,7 @@ from pathlib import Path from functools import partial from qiskit.providers.aer import AerSimulator +import pytest from qokit.maxcut import maxcut_obj, get_adjacency_matrix, get_maxcut_terms @@ -17,11 +18,13 @@ from qokit.utils import brute_force, precompute_energies from qokit.parameter_utils import get_sk_gamma_beta, get_fixed_gamma_beta import qokit +from qokit.fur import get_available_simulators test_maxcut_folder = Path(__file__).parent qiskit_backend = AerSimulator(method="statevector") +SIMULATORS = get_available_simulators("x") def test_maxcut_obj(): @@ -81,14 +84,14 @@ def test_maxcut_weighted_qaoa_obj(): assert np.isclose(precomputed_cuts.dot(np.abs(sv) ** 2), row["Expected cut of QAOA"]) -def test_maxcut_precompute(): +@pytest.mark.parametrize("simclass", SIMULATORS) +def test_maxcut_precompute(simclass): N = 4 G = nx.random_regular_graph(3, N) print(G.edges()) for u, v, w in G.edges(data=True): w["weight"] = np.random.rand() precomputed_cuts = precompute_energies(maxcut_obj, N, w=get_adjacency_matrix(G)) - simclass = qokit.fur.choose_simulator("gpu") terms = get_maxcut_terms(G) sim = simclass(N, terms=terms) cuts = sim.get_cost_diagonal() From 6357f4254fe2976465b62be87b319bfe912e5a82 Mon Sep 17 00:00:00 2001 From: "Shaydulin, Ruslan" Date: Sat, 9 Sep 2023 16:40:40 +0000 Subject: [PATCH 5/6] add docstring --- qokit/fur/__init__.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/qokit/fur/__init__.py b/qokit/fur/__init__.py index 159057f6c..30422582a 100644 --- a/qokit/fur/__init__.py +++ b/qokit/fur/__init__.py @@ -29,7 +29,18 @@ } -def get_available_simulator_names(type="x"): +def get_available_simulator_names(type: str = "x") -> list: + """ + Return names of available simulators + + Parameters + ---------- + type: type of QAOA mixer to simulate + + Returns + ------- + List of available simulators + """ family = SIMULATORS.get(type, None) if family is None: raise ValueError(f"Unknown simulator type: {type}") @@ -45,7 +56,18 @@ def get_available_simulator_names(type="x"): return available -def get_available_simulators(type="x"): +def get_available_simulators(type: str = "x") -> list: + """ + Return (uninitialized) classes of available simulators + + Parameters + ---------- + type: type of QAOA mixer to simulate + + Returns + ------- + List of available simulators + """ available_names = get_available_simulator_names(type=type) return [SIMULATORS[type][s] for s in available_names] From d3f35248fd06f212a6d8994d0a7af8ad618f4b6c Mon Sep 17 00:00:00 2001 From: "Shaydulin, Ruslan" Date: Sat, 9 Sep 2023 16:46:30 +0000 Subject: [PATCH 6/6] now runs on all available simulators --- tests/test_maxcut.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_maxcut.py b/tests/test_maxcut.py index 60e5469f1..395416bcb 100644 --- a/tests/test_maxcut.py +++ b/tests/test_maxcut.py @@ -24,7 +24,7 @@ qiskit_backend = AerSimulator(method="statevector") -SIMULATORS = get_available_simulators("x") +SIMULATORS = get_available_simulators("x") + get_available_simulators("xyring") + get_available_simulators("xycomplete") def test_maxcut_obj(): @@ -95,7 +95,7 @@ def test_maxcut_precompute(simclass): terms = get_maxcut_terms(G) sim = simclass(N, terms=terms) cuts = sim.get_cost_diagonal() - assert np.allclose(precomputed_cuts, cuts, atol=1e-7) + assert np.allclose(precomputed_cuts, cuts, atol=1e-6) def test_sk_ini_maxcut():