Skip to content

Commit

Permalink
Merge branch 'main' of github.com:Infleqtion/client-superstaq into nu…
Browse files Browse the repository at this point in the history
…mpy2-types
  • Loading branch information
richrines1 committed Oct 14, 2024
2 parents 5959b49 + 7a7ea26 commit 1bfabb6
Show file tree
Hide file tree
Showing 6 changed files with 12 additions and 140 deletions.
2 changes: 1 addition & 1 deletion checks-superstaq/checks_superstaq/checks-pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ enable = [
accept-no-param-doc=false # Do not accept totally missing parameter documentation.
accept-no-raise-doc=false # Do not accept totally missing raises documentation.
accept-no-return-doc=false # Do not accept totally missing return documentation.
accept-no-yield-doc=false # Do not accept missing yields annotations.
accept-no-yields-doc=false # Do not accept missing yields annotations.
default-docstring-type = "google"

# Ignore long lines containing urls or pylint directives.
Expand Down
119 changes: 4 additions & 115 deletions supermarq-benchmarks/supermarq/benchmarks/qaoa_fermionic_swap_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@

from __future__ import annotations

from collections.abc import Mapping

import cirq
import numpy as np
import numpy.typing as npt
import scipy

import supermarq
from supermarq.benchmark import Benchmark
from supermarq.benchmarks.qaoa_vanilla_proxy import QAOAVanillaProxy


class QAOAFermionicSwapProxy(Benchmark):
class QAOAFermionicSwapProxy(QAOAVanillaProxy):
"""Proxy of a full Quantum Approximate Optimization Algorithm (QAOA) benchmark.
This benchmark targets MaxCut on a Sherrington-Kirkpatrick (SK) model. Device
Expand All @@ -34,28 +29,7 @@ class QAOAFermionicSwapProxy(Benchmark):
#. Finding approximately optimal angles (rather than random values)
"""

def __init__(self, num_qubits: int) -> None:
"""Generate a new benchmark instance.
Args:
num_qubits: The number of nodes (qubits) within the SK graph.
"""
self.num_qubits = num_qubits
self.hamiltonian = self._gen_sk_hamiltonian()
self.params = self._gen_angles()

def _gen_sk_hamiltonian(self) -> list[tuple[int, int, float]]:
"""Randomly pick +1 or -1 for each edge weight."""
hamiltonian = []
for i in range(self.num_qubits):
for j in range(i + 1, self.num_qubits):
hamiltonian.append((i, j, np.random.choice([-1, 1])))

np.random.shuffle(hamiltonian)

return hamiltonian

def _gen_swap_network(self, gamma: float, beta: float) -> cirq.Circuit:
def _gen_ansatz(self, gamma: float, beta: float) -> cirq.Circuit:
qubits = cirq.LineQubit.range(self.num_qubits)
circuit = cirq.Circuit()

Expand Down Expand Up @@ -105,90 +79,5 @@ def _gen_swap_network(self, gamma: float, beta: float) -> cirq.Circuit:
return circuit

def _get_energy_for_bitstring(self, bitstring: str) -> float:
energy_val = 0.0
for i, j, weight in self.hamiltonian:
if bitstring[i] == bitstring[j]:
energy_val -= weight # if edge is UNCUT, weight counts against objective
else:
energy_val += weight # if edge is CUT, weight counts towards objective
return energy_val

def _get_expectation_value_from_probs(self, probabilities: Mapping[str, float]) -> float:
expectation_value = 0.0
for bitstring, probability in probabilities.items():
expectation_value += probability * self._get_energy_for_bitstring(bitstring)
return expectation_value

def _get_opt_angles(self) -> tuple[npt.NDArray[np.float64], float]:
def f(params: npt.NDArray[np.float64]) -> float:
"""The objective function to minimize.
Args:
params: The parameters at which to evaluate the objective.
Returns:
Evaluation of objective given parameters.
"""
gamma, beta = params
circ = self._gen_swap_network(gamma, beta)
# Reverse bitstring ordering due to SWAP network
raw_probs = supermarq.simulation.get_ideal_counts(circ)
probs = {bitstring[::-1]: probability for bitstring, probability in raw_probs.items()}
h_expect = self._get_expectation_value_from_probs(probs)

return -h_expect # because we are minimizing instead of maximizing

init_params = [np.random.uniform() * 2 * np.pi, np.random.uniform() * 2 * np.pi]
out = scipy.optimize.minimize(f, init_params, method="COBYLA")

return out["x"], out["fun"]

def _gen_angles(self) -> npt.NDArray[np.float64]:
# Classically simulate the variational optimization 5 times,
# return the parameters from the best performing simulation
best_params, best_cost = np.zeros(2), 0.0
for _ in range(5):
params, cost = self._get_opt_angles()
if cost < best_cost:
best_params = params
best_cost = cost
return best_params

def circuit(self) -> cirq.Circuit:
"""Generate a QAOA circuit for the Sherrington-Kirkpatrick model.
This particular benchmark utilizes a quantum circuit structure called
the fermionic swap network. We restrict the depth of this proxy benchmark
to p=1 to keep the classical simulation scalable.
Returns:
The S-K model QAOA `cirq.Circuit`.
"""
gamma, beta = self.params
return self._gen_swap_network(gamma, beta)

def score(self, counts: Mapping[str, float]) -> float:
"""Compare the experimental output to the output of noiseless simulation.
The implementation here has exponential runtime and would not scale. However, it could in
principle be done efficiently via https://arxiv.org/abs/1706.02998, so we're good.
Args:
counts: A dictionary containing the measurement counts from circuit execution.
Returns:
The QAOA Fermionic SWAP proxy benchmark score.
"""
# Reverse bitstring ordering due to SWAP network
raw_probs = supermarq.simulation.get_ideal_counts(self.circuit())
ideal_counts = {
bitstring[::-1]: probability for bitstring, probability in raw_probs.items()
}
total_shots = sum(counts.values())
# Reverse the order of the bitstrings due to the fermionic swap ansatz
experimental_counts = {k[::-1]: v / total_shots for k, v in counts.items()}

ideal_value = self._get_expectation_value_from_probs(ideal_counts)
experimental_value = self._get_expectation_value_from_probs(experimental_counts)

return 1 - abs(ideal_value - experimental_value) / (2 * ideal_value)
return super()._get_energy_for_bitstring(bitstring[::-1])
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def f(params: npt.NDArray[np.float64]) -> float:
"""The objective function to minimize.
Args:
params: parameters for objective.
params: The parameters at which to evaluate the objective.
Returns:
Evaluation of objective given parameters.
Expand Down Expand Up @@ -137,7 +137,7 @@ def circuit(self) -> cirq.Circuit:
the classical simulation scalable.
Returns:
The S-K model QAOA circuit.
The S-K model QAOA `cirq.Circuit`.
"""
gamma, beta = self.params
return self._gen_ansatz(gamma, beta)
Expand All @@ -149,10 +149,10 @@ def score(self, counts: Mapping[str, float]) -> float:
principle be done efficiently via https://arxiv.org/abs/1706.02998, so we're good.
Args:
counts: A dictionary containing measurement counts from circuit execution.
counts: A dictionary containing the measurement counts from circuit execution.
Returns:
The QAOA Vanilla proxy benchmark score.
The QAOA proxy benchmark score.
"""
ideal_counts = supermarq.simulation.get_ideal_counts(self.circuit())
total_shots = sum(counts.values())
Expand Down
2 changes: 1 addition & 1 deletion supermarq-benchmarks/supermarq/qcvv/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


@pytest.fixture(scope="session", autouse=True)
def patch_tqdm() -> Generator[None, None, None]:
def _patch_tqdm() -> Generator[None, None, None]:
"""Disable progress bars during tests."""
with mock.patch.dict(os.environ, {"TQDM_DISABLE": "1"}):
yield
2 changes: 0 additions & 2 deletions supermarq-benchmarks/supermarq/qcvv/irb.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,6 @@ def random_two_qubit_clifford(self) -> cirq.CliffordGate:
qubits = cirq.LineQubit.range(2)
a = self.random_single_qubit_clifford()
b = self.random_single_qubit_clifford()
print(a)
print(b)
idx = self._rng.integers(20)
if idx == 0:
return cirq.CliffordGate.from_op_list([a(qubits[0]), b(qubits[1])], qubits)
Expand Down
19 changes: 2 additions & 17 deletions supermarq-benchmarks/supermarq/qcvv/irb_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from unittest.mock import MagicMock, patch

import cirq
import numpy as np
import pandas as pd
import pytest

Expand Down Expand Up @@ -81,27 +80,13 @@ def test_irb_random_clifford() -> None:
assert gate.num_qubits() == 2


def test_random_two_qubit_clifford() -> None:
with patch("numpy.random.default_rng", side_effect=np.random.default_rng) as rng:
rng.return_value.integers.side_effect = range(20)
exp = IRB()

gates: set[cirq.Gate] = set()
for _ in range(20):
gate = exp.random_two_qubit_clifford()
assert isinstance(gate, cirq.ops.CliffordGate)
assert gate.num_qubits() == 2
assert gate not in gates
gates.add(gate)


def test_gates_per_clifford() -> None:
exp = IRB()
exp = IRB(random_seed=1)
gates = exp.gates_per_clifford(samples=1000)
assert gates["single_qubit_gates"] == pytest.approx(0.95, abs=0.1)
assert gates["two_qubit_gates"] == 0.0

exp = IRB(interleaved_gate=cirq.CZ)
exp = IRB(interleaved_gate=cirq.CZ, random_seed=1)
gates = exp.gates_per_clifford(samples=1000)
assert gates["single_qubit_gates"] == pytest.approx(4.5, abs=0.25)
assert gates["two_qubit_gates"] == pytest.approx(1.5, abs=0.1)
Expand Down

0 comments on commit 1bfabb6

Please sign in to comment.