Skip to content

Commit

Permalink
Merge pull request #75 from zapatacomputing/dev
Browse files Browse the repository at this point in the history
Merge dev to main
  • Loading branch information
max-radin authored Nov 24, 2021
2 parents d3ac854 + 261a449 commit 7b0cb0e
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 24 deletions.
14 changes: 11 additions & 3 deletions src/python/qeqiskit/backend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ def __init__(
"""Get a qiskit QPU that adheres to the
zquantum.core.interfaces.backend.QuantumBackend
qiskit currently offers 2 types of qasm simulators:
1. qasm_simulator - a local simulator that is depreciated.
2. IBMQ_qasm_simulator - a remote simulator.
All implementation of qasm_simulator have been removed since it's depreciation
but IBMQ_qasm_simulator is still tested by this module.
Args:
device_name: the name of the device
hub: IBMQ hub
Expand Down Expand Up @@ -237,7 +243,7 @@ def aggregregate_measurements(

def run_circuitset_and_measure(
self,
circuitset: List[Circuit],
circuits: List[Circuit],
n_samples: List[int],
) -> List[Measurements]:
"""Run a set of circuits and measure a certain number of bitstrings.
Expand All @@ -252,11 +258,13 @@ def run_circuitset_and_measure(
A list of Measurements objects containing the observed bitstrings.
"""

assert isinstance(n_samples, list)
assert isinstance(n_samples[0], int)
(
experiments,
n_samples_for_experiments,
multiplicities,
) = self.transform_circuitset_to_ibmq_experiments(circuitset, n_samples)
) = self.transform_circuitset_to_ibmq_experiments(circuits, n_samples)
batches, n_samples_for_batches = self.batch_experiments(
experiments, n_samples_for_experiments
)
Expand All @@ -266,7 +274,7 @@ def run_circuitset_and_measure(
for n_samples, batch in zip(n_samples_for_batches, batches)
]

self.number_of_circuits_run += len(circuitset)
self.number_of_circuits_run += len(circuits)
self.number_of_jobs_run += len(batches)

return self.aggregregate_measurements(jobs, batches, multiplicities)
Expand Down
6 changes: 5 additions & 1 deletion src/python/qeqiskit/optimizer/optimizer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Dict, Optional

import numpy as np
from qiskit.algorithms.optimizers import ADAM, SPSA
from qiskit.algorithms.optimizers import ADAM, NFT, SPSA
from scipy.optimize import OptimizeResult
from zquantum.core.history.recorder import recorder as _recorder
from zquantum.core.interfaces.functions import CallableWithGradient
Expand Down Expand Up @@ -43,6 +43,8 @@ def __init__(
if self.method == "AMSGRAD":
self.optimizer_kwargs["amsgrad"] = True
self.optimizer = ADAM(**self.optimizer_kwargs)
elif self.method == "NFT":
self.optimizer = NFT(**self.optimizer_kwargs)

def _minimize(
self,
Expand Down Expand Up @@ -78,6 +80,8 @@ def _minimize(

if self.method == "ADAM" or self.method == "AMSGRAD":
nit = self.optimizer._t
elif self.method == "NFT":
nit = self.optimizer._options["maxiter"]
else:
nit = self.optimizer.maxiter

Expand Down
5 changes: 4 additions & 1 deletion src/python/qeqiskit/simulator/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def run_circuit_and_measure(self, circuit: Circuit, n_samples: int) -> Measureme
if self.device_connectivity is not None:
coupling_map = CouplingMap(self.device_connectivity.connectivity)

if self.device_name == "statevector_simulator":
if self.device_name == "aer_simulator_statevector":
wavefunction = self.get_wavefunction(circuit)
return Measurements(sample_from_wavefunction(wavefunction, n_samples))
else:
Expand Down Expand Up @@ -158,6 +158,9 @@ def _get_wavefunction_from_native_circuit(
if self.device_connectivity is not None:
coupling_map = CouplingMap(self.device_connectivity.connectivity)

if self.device_name == "aer_simulator_statevector":
ibmq_circuit.save_state()

# Execute job to get wavefunction
job = execute(
ibmq_circuit,
Expand Down
2 changes: 1 addition & 1 deletion tests/qeqiskit/backend/backend_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def test_run_circuitset_and_measure_split_circuits_and_jobs(self, backend):
# Given
num_circuits = 200
circuit = self.x_cnot_circuit()
n_samples = 8193
n_samples = 20001

# Verify that we are actually going to need to split circuits
assert n_samples > backend.max_shots
Expand Down
69 changes: 67 additions & 2 deletions tests/qeqiskit/optimizer/optimizer_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import numpy as np
import pytest
from qeqiskit.optimizer import QiskitOptimizer
from zquantum.core.history.recorder import recorder
from zquantum.core.interfaces.optimizer_test import OptimizerTests
from zquantum.core.gradients import finite_differences_gradient
from zquantum.core.interfaces.functions import FunctionWithGradient
from zquantum.core.interfaces.optimizer_test import (
MANDATORY_OPTIMIZATION_RESULT_FIELDS,
OptimizerTests,
)


@pytest.fixture(
Expand All @@ -26,6 +30,18 @@ def optimizer(request):
return QiskitOptimizer(**request.param)


@pytest.fixture(
params=[
{
"method": "NFT",
"optimizer_kwargs": {"maxiter": 2e5},
},
]
)
def sinusoidal_optimizer(request):
return QiskitOptimizer(**request.param)


@pytest.fixture(params=[True, False])
def keep_history(request):
return request.param
Expand All @@ -46,3 +62,52 @@ def test_optimizer_succeeds_on_cost_function_without_gradient(
assert "opt_value" in results
assert "opt_params" in results
assert "history" in results


class TestQiskitSinusoidalOptimizerTests(OptimizerTests):
"""Some optimizers assume that the cost function can be expressed as a sum of
products of sines and cosines (Eq. 12 of arXiv:1903.12166), a functional form which
reflects the cost function of many variational quantum algorithms. Such optimizers
may fail to find the minimum of cost functions that are not of this form.
This class removes the tests from the base class that check whether the optimizer
finds the minimum of such non-sinusoidal cost functions, and replaces them with a
test on a sinuisoidal cost function."""

@pytest.fixture
def sum_cos_x(self):
"""Create a sinuisoidal function whose minimum is at the origin with a value of
zero."""
return lambda x: -np.sum(np.cos(x)) + x.shape[0]

@pytest.mark.skip(reason="Optimizer only supports sinusoidal cost functions")
def test_optimizer_succeeds_with_optimizing_rosenbrock_function(self):
pass

@pytest.mark.skip(reason="Optimizer only supports sinusoidal cost functions")
def test_optimizer_succeeds_with_optimizing_sum_of_squares_function(self):
pass

@pytest.mark.skip(reason="Optimizer only supports sinusoidal cost functions")
def test_optimizer_succeeds_on_cost_function_without_gradient(self):
pass

def test_optimizer_succeeds_on_sum_of_cosines_function(
self, sinusoidal_optimizer, sum_cos_x, keep_history
):

cost_function = FunctionWithGradient(
sum_cos_x, finite_differences_gradient(sum_cos_x)
)

results = sinusoidal_optimizer.minimize(
cost_function, initial_params=np.array([1, -1]), keep_history=keep_history
)

assert results.opt_value == pytest.approx(0, abs=1e-5)
assert results.opt_params == pytest.approx(np.zeros(2), abs=1e-4)

assert all(field in results for field in MANDATORY_OPTIMIZATION_RESULT_FIELDS)

assert "history" in results or not keep_history
assert "gradient_history" in results or not keep_history
32 changes: 16 additions & 16 deletions tests/qeqiskit/simulator/simulator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
@pytest.fixture(
params=[
{
"device_name": "qasm_simulator",
"device_name": "aer_simulator",
"api_token": os.getenv("ZAPATA_IBMQ_API_TOKEN"),
},
]
Expand All @@ -30,7 +30,7 @@ def backend(request):
@pytest.fixture(
params=[
{
"device_name": "statevector_simulator",
"device_name": "aer_simulator_statevector",
},
]
)
Expand All @@ -41,10 +41,10 @@ def wf_simulator(request):
@pytest.fixture(
params=[
{
"device_name": "qasm_simulator",
"device_name": "aer_simulator",
},
{
"device_name": "statevector_simulator",
"device_name": "aer_simulator_statevector",
},
]
)
Expand All @@ -54,7 +54,7 @@ def sampling_simulator(request):

@pytest.fixture(
params=[
{"device_name": "qasm_simulator", "optimization_level": 0},
{"device_name": "aer_simulator", "optimization_level": 0},
]
)
def noisy_simulator(request):
Expand Down Expand Up @@ -96,16 +96,16 @@ def test_run_circuitset_and_measure(self, sampling_simulator):
assert all(bitstring == (1, 0, 0) for bitstring in measurements.bitstrings)

def test_setup_basic_simulators(self):
simulator = QiskitSimulator("qasm_simulator")
simulator = QiskitSimulator("aer_simulator")
assert isinstance(simulator, QiskitSimulator)
assert simulator.device_name == "qasm_simulator"
assert simulator.device_name == "aer_simulator"
assert simulator.noise_model is None
assert simulator.device_connectivity is None
assert simulator.basis_gates is None

simulator = QiskitSimulator("statevector_simulator")
simulator = QiskitSimulator("aer_simulator_statevector")
assert isinstance(simulator, QiskitSimulator)
assert simulator.device_name == "statevector_simulator"
assert simulator.device_name == "aer_simulator_statevector"
assert simulator.noise_model is None
assert simulator.device_connectivity is None
assert simulator.basis_gates is None
Expand Down Expand Up @@ -139,7 +139,7 @@ def test_expectation_value_with_noisy_simulator(self, noisy_simulator):
assert expectation_values_10_gates.values[0] > -1
assert expectation_values_10_gates.values[0] < 0.0
assert isinstance(noisy_simulator, QiskitSimulator)
assert noisy_simulator.device_name == "qasm_simulator"
assert noisy_simulator.device_name == "aer_simulator"
assert isinstance(noisy_simulator.noise_model, AerNoise.NoiseModel)
assert noisy_simulator.device_connectivity is not None
assert noisy_simulator.basis_gates is not None
Expand Down Expand Up @@ -167,7 +167,7 @@ def test_expectation_value_with_noisy_simulator(self, noisy_simulator):
> expectation_values_10_gates.values[0]
)
assert isinstance(noisy_simulator, QiskitSimulator)
assert noisy_simulator.device_name == "qasm_simulator"
assert noisy_simulator.device_name == "aer_simulator"
assert isinstance(noisy_simulator.noise_model, AerNoise.NoiseModel)
assert noisy_simulator.device_connectivity is not None
assert noisy_simulator.basis_gates is not None
Expand All @@ -179,7 +179,7 @@ def test_optimization_level_of_transpiler(self):
)
n_samples = 8192
simulator = QiskitSimulator(
"qasm_simulator",
"aer_simulator",
noise_model=noise_model,
device_connectivity=connectivity,
optimization_level=0,
Expand Down Expand Up @@ -213,8 +213,8 @@ def test_run_circuit_and_measure_seed(self):
# Given
circuit = Circuit([X(0), CNOT(1, 2)])
n_samples = 100
simulator1 = QiskitSimulator("qasm_simulator", seed=643)
simulator2 = QiskitSimulator("qasm_simulator", seed=643)
simulator1 = QiskitSimulator("aer_simulator", seed=643)
simulator2 = QiskitSimulator("aer_simulator", seed=643)

# When
measurements1 = simulator1.run_circuit_and_measure(circuit, n_samples)
Expand All @@ -227,8 +227,8 @@ def test_run_circuit_and_measure_seed(self):
def test_get_wavefunction_seed(self):
# Given
circuit = Circuit([X(0), CNOT(1, 2)])
simulator1 = QiskitSimulator("statevector_simulator", seed=643)
simulator2 = QiskitSimulator("statevector_simulator", seed=643)
simulator1 = QiskitSimulator("aer_simulator_statevector", seed=643)
simulator2 = QiskitSimulator("aer_simulator_statevector", seed=643)

# When
wavefunction1 = simulator1.get_wavefunction(circuit)
Expand Down

0 comments on commit 7b0cb0e

Please sign in to comment.