Skip to content

Commit

Permalink
Merge pull request #539 from qiboteam/testplatforms
Browse files Browse the repository at this point in the history
Test all available platforms automatically
  • Loading branch information
scarrazza committed Feb 2, 2022
2 parents 30e0ba2 + df7a3e0 commit 790c18e
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
pylint src -E -d E1123,E1120
- name: Test with pytest core
run: |
pytest --cov=qibo --cov-report=xml --pyargs qibo --durations=60
pytest src/qibo/tests/ --skip-parallel --cov=qibo --cov-report=xml --pyargs qibo --durations=60
- name: Test documentation examples
if: startsWith(matrix.os, 'ubuntu')
run: |
Expand Down
2 changes: 1 addition & 1 deletion src/qibo/core/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ def cache(self):
cache = K.create_gate_cache(self)

qubits = sorted(self.nqubits - q - 1 for q in self.target_qubits)
cache.qubits_tensor = qubits + [q + self.nqubits for q in qubits]
cache.qubits_tensor = K.cast(qubits + [q + self.nqubits for q in qubits], dtype="int32")
cache.target_qubits_dm = self.qubits + tuple(q + self.nqubits for q in self.qubits)

if not K.is_custom:
Expand Down
2 changes: 1 addition & 1 deletion src/qibo/optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def newtonian(loss, initial_parameters, args=(), method='Powell',
``scipy.optimize.minimize``.
processes (int): number of processes when using the parallel BFGS method.
"""
if method == 'parallel_L-BFGS-B':
if method == 'parallel_L-BFGS-B': # pragma: no cover
from qibo.parallel import _check_parallel_configuration
_check_parallel_configuration(processes)
o = ParallelBFGS(loss, args=args, processes=processes,
Expand Down
8 changes: 4 additions & 4 deletions src/qibo/parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _executor(params): # pragma: no cover
return ParallelResources().run(params)


def parallel_execution(circuit, states, processes=None):
def parallel_execution(circuit, states, processes=None): # pragma: no cover
"""Execute circuit for multiple states.
Example:
Expand Down Expand Up @@ -128,12 +128,12 @@ def operation(state, circuit): # pragma: no cover
return results


def parallel_parametrized_execution(circuit, parameters, initial_state=None, processes=None):
def parallel_parametrized_execution(circuit, parameters, initial_state=None, processes=None): # pragma: no cover
"""Execute circuit for multiple parameters and fixed initial_state.
Example:
.. testcode::
import qibo
original_backend = qibo.get_backend()
qibo.set_backend("qibotf")
Expand Down Expand Up @@ -191,7 +191,7 @@ def operation(params, circuit, state): # pragma: no cover
return results


def _check_parallel_configuration(processes):
def _check_parallel_configuration(processes): # pragma: no cover
"""Check if configuration is suitable for efficient parallel execution."""
import sys, psutil
from qibo import get_device, get_backend, get_threads
Expand Down
93 changes: 63 additions & 30 deletions src/qibo/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
import sys
import pytest
import qibo
from qibo import K

_available_backends = set(b.get('name') for b in qibo.K.profile.get('backends')
_available_backends = set(b.get('name') for b in K.profile.get('backends')
if (not b.get('is_hardware', False) and
qibo.K.check_availability(b.get('name'))))
K.check_availability(b.get('name'))))
_available_backends.add("numpy")
_ACCELERATORS = None
for bkd in _available_backends:
if qibo.K.construct_backend(bkd).supports_multigpu:
_ACCELERATORS = "2/GPU:0,1/GPU:0+1/GPU:1,2/GPU:0+1/GPU:1+1/GPU:2"
_ACCELERATORS = "2/GPU:0,1/GPU:0+1/GPU:1,2/GPU:0+1/GPU:1+1/GPU:2"
_BACKENDS = ",".join(_available_backends)


Expand Down Expand Up @@ -43,14 +41,23 @@ def pytest_addoption(parser):
# `test_backends_agreement.py` tests that backend methods agree between
# different backends by testing each backend in `--backends` with the
# `--target-backend`
parser.addoption("--skip-parallel", action="store_true",
help="Skip tests that use the ``qibo.parallel`` module.")
# parallel tests make the CI hang


@pytest.fixture
def backend(backend_name):
def backend(backend_platform):
if "-" in backend_platform:
backend_name, platform_name = backend_platform.split("-")
else:
backend_name, platform_name = backend_platform, None

original_backend = qibo.get_backend()
qibo.set_backend(backend_name)
original_platform = K.get_platform()
qibo.set_backend(backend_name, platform=platform_name)
yield
qibo.set_backend(original_backend)
qibo.set_backend(original_backend, platform=original_platform)


def pytest_generate_tests(metafunc):
Expand All @@ -70,11 +77,32 @@ def pytest_generate_tests(metafunc):
the user when calling `pytest`.
"""
backends = metafunc.config.option.backends.split(",")
accelerators = metafunc.config.option.accelerators
# construct backend instances to find what platforms each backend supports
# and if it supports multi-GPU
backend_platforms = [] # list of all available backend-platform pairs
distributed_backends = [] # list of backends that support multi-GPU
for name in backends:
instance = K.construct_backend(name)
for platform in instance.available_platforms:
if platform is not None:
total_name = f"{name}-{platform}"
instance.set_platform(platform)
else:
total_name = name
backend_platforms.append(total_name)
if instance.supports_multigpu:
distributed_backends.append(total_name)

# If a GPU platform is not available, we execute distributed tests with
# `qibojit-numba` for coverage purposes
#if not distributed_backends and "qibojit-numba" in backend_platforms: # pragma: no cover
# distributed_backends.append("qibojit-numba")

# parse accelerator stings to dicts
if accelerators is not None:
accelerators = [{dev[1:]: int(dev[0]) for dev in x.split("+")}
for x in accelerators.split(",")]
accelerators = metafunc.config.option.accelerators
accelerators = [{dev[1:]: int(dev[0]) for dev in x.split("+")}
for x in accelerators.split(",")]

distributed_tests = {
"qibo.tests.test_core_states_distributed",
"qibo.tests.test_core_distutils",
Expand All @@ -83,42 +111,47 @@ def pytest_generate_tests(metafunc):
}
module_name = metafunc.module.__name__
# skip distributed tests if qibojit or qibotf are not installed
if ((module_name in distributed_tests) and ("qibotf" not in backends) and
("qibojit" not in backends)): # pragma: no cover
if module_name in distributed_tests and not distributed_backends: # pragma: no cover
pytest.skip("Skipping distributed tests because are not supported by "
"the available backends.")
# skip distributed tests on mac
if sys.platform == "darwin": # pragma: no cover
accelerators = None
if module_name in distributed_tests:
pytest.skip("macos does not support distributed circuits.")
# macos does not support distributed circuits
distributed_backends = []

if module_name in distributed_tests and not distributed_backends: # pragma: no cover
pytest.skip("Skipping distributed tests because are not supported by "
"available backends.")

# skip parallel tests if the ``--skip-parallel`` option is used
skip_parallel = metafunc.config.option.skip_parallel
if "skip_parallel" in metafunc.fixturenames:
metafunc.parametrize("skip_parallel", [skip_parallel])

# for `test_backends_agreement.py`
if "tested_backend" in metafunc.fixturenames:
target = metafunc.config.option.target_backend
test_backends = [x for x in backends if x != target and x not in qibo.K.hardware_backends]
metafunc.parametrize("tested_backend", test_backends)
metafunc.parametrize("tested_backend", [x for x in backends if x != target])
metafunc.parametrize("target_backend", [target])

if "backend_name" in metafunc.fixturenames:
metafunc.parametrize("backend_name", backends)

if "backend_platform" in metafunc.fixturenames:
if metafunc.module.__name__ in distributed_tests:
distributed_backends = list(set(backends) & {"qibotf", "qibojit"})
metafunc.parametrize("backend_name", distributed_backends)
metafunc.parametrize("backend_platform", distributed_backends)
if "accelerators" in metafunc.fixturenames:
metafunc.parametrize("accelerators", accelerators)

elif "accelerators" in metafunc.fixturenames:
if accelerators is None: # pragma: no cover
# `accelerators` is never `None` in CI test execution
metafunc.parametrize("backend_name", backends)
metafunc.parametrize("backend_platform", backend_platforms)
metafunc.parametrize("accelerators", [None])
else:
config = [(b, None) for b in backends]
if "qibotf" in backends:
config.extend(("qibotf", d) for d in accelerators)
if "qibojit" in backends:
config.extend(("qibojit", d) for d in accelerators)
metafunc.parametrize("backend_name,accelerators", config)
config = [(b, None) for b in backend_platforms]
config.extend((b, a) for b in distributed_backends for a in accelerators)
metafunc.parametrize("backend_platform,accelerators", config)

else:
metafunc.parametrize("backend_name", backends)
metafunc.parametrize("backend_platform", backend_platforms)
9 changes: 6 additions & 3 deletions src/qibo/tests/test_cirq.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ def assert_gates_equivalent(qibo_gate, cirq_gates, nqubits,
if ndevices is not None:
accelerators = {"/GPU:0": ndevices}

if accelerators and not K.supports_multigpu:
with pytest.raises(NotImplementedError):
c = models.Circuit(nqubits, accelerators)
if accelerators:
if not K.supports_multigpu:
with pytest.raises(NotImplementedError):
c = models.Circuit(nqubits, accelerators)
elif K.get_platform() == "numba" and len(K.available_platforms) > 1: # pragma: no cover
pytest.skip("Skipping distributed cirq test for numba platform.")
else:
c = models.Circuit(nqubits, accelerators)
c.add(qibo_gate)
Expand Down
6 changes: 3 additions & 3 deletions src/qibo/tests/test_core_distcircuit_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_distributed_circuit_execution_pretransformed(backend, accelerators):
initial_state = random_state(c.nqubits)
final_state = dist_c(np.copy(initial_state))
target_state = c(np.copy(initial_state))
K.assert_allclose(target_state, final_state)
K.assert_allclose(target_state, final_state, atol=1e-7)


def test_distributed_circuit_execution_with_swap(backend, accelerators):
Expand All @@ -55,7 +55,7 @@ def test_distributed_circuit_execution_with_swap(backend, accelerators):
initial_state = random_state(c.nqubits)
final_state = dist_c(np.copy(initial_state))
target_state = c(np.copy(initial_state))
K.assert_allclose(target_state, final_state)
K.assert_allclose(target_state, final_state, atol=1e-7)


def test_distributed_circuit_execution_special_gate(backend, accelerators):
Expand Down Expand Up @@ -102,7 +102,7 @@ def test_distributed_circuit_execution_controlled_by_gates(backend, accelerators
initial_state = random_state(c.nqubits)
final_state = dist_c(np.copy(initial_state))
target_state = c(np.copy(initial_state))
K.assert_allclose(target_state, final_state)
K.assert_allclose(target_state, final_state, atol=1e-7)


def test_distributed_circuit_execution_addition(backend, accelerators):
Expand Down
2 changes: 1 addition & 1 deletion src/qibo/tests/test_core_fusion.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def test_random_circuit_fusion(backend, nqubits, ngates):
q0, q1 = np.random.randint(0, nqubits, (2,))
c.add(gate(q0, q1))
fused_c = c.fuse()
K.assert_allclose(fused_c(), c())
K.assert_allclose(fused_c(), c(), atol=1e-7)


def test_controlled_by_gates_fusion(backend):
Expand Down
10 changes: 6 additions & 4 deletions src/qibo/tests/test_models_qgan.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,16 @@ def set_params(circuit, params, x_input, i):
qibo.set_backend(original_backend)


def test_qgan_errors():
def test_qgan_errors(backend_name):
if not K.check_availability("tensorflow"): # pragma: no cover
pytest.skip("Skipping StyleQGAN test because tensorflow backend is not available.")

original_backend = qibo.get_backend()
qibo.set_backend("qibojit")
with pytest.raises(RuntimeError):
qgan = models.StyleQGAN(latent_dim=2)

if backend_name != "tensorflow":
qibo.set_backend(backend_name)
with pytest.raises(RuntimeError):
qgan = models.StyleQGAN(latent_dim=2)

qibo.set_backend("tensorflow")
with pytest.raises(ValueError):
Expand Down
37 changes: 17 additions & 20 deletions src/qibo/tests/test_models_variational.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,19 @@ def myloss(parameters, circuit, target):
("sgd", {"nepochs": 5}, False, None),
("sgd", {"nepochs": 5}, True, None)]
@pytest.mark.parametrize(test_names, test_values)
def test_vqe(backend, method, options, compile, filename):
def test_vqe(backend, method, options, compile, filename, skip_parallel):
"""Performs a VQE circuit minimization test."""
original_threads = qibo.get_threads()
if (method == "sgd" or compile) and qibo.get_backend() != "tensorflow":
pytest.skip("Skipping SGD test for unsupported backend.")

if method == 'parallel_L-BFGS-B':
device = qibo.get_device()
backend = qibo.get_backend()
if backend == "tensorflow" or backend == "qibojit" or "GPU" in device:
pytest.skip("unsupported configuration")
import sys
if sys.platform == 'win32' or sys.platform == 'darwin': # pragma: no cover
pytest.skip("Parallel L-BFGS-B only supported on linux.")
if method == 'parallel_L-BFGS-B': # pragma: no cover
if skip_parallel:
pytest.skip("Skipping parallel test.")
from qibo.tests.test_parallel import is_parallel_supported
backend_name = qibo.get_backend()
if not is_parallel_supported(backend_name):
pytest.skip("Skipping parallel test due to unsupported configuration.")
qibo.set_threads(1)

nqubits = 6
Expand Down Expand Up @@ -313,18 +312,17 @@ def __call__(self, x):
("cma", {"maxfevals": 2}, False, None),
("parallel_L-BFGS-B", {'maxiter': 1}, False, None)]
@pytest.mark.parametrize(test_names, test_values)
def test_aavqe(backend, method, options, compile, filename):
def test_aavqe(backend, method, options, compile, filename, skip_parallel):
"""Performs a AAVQE circuit minimization test."""
original_threads = qibo.get_threads()

if method == 'parallel_L-BFGS-B':
device = qibo.get_device()
backend = qibo.get_backend()
if backend == "tensorflow" or backend == "qibojit" or "GPU" in device:
pytest.skip("unsupported configuration")
import sys
if sys.platform == 'win32' or sys.platform == 'darwin': # pragma: no cover
pytest.skip("Parallel L-BFGS-B only supported on linux.")
if method == 'parallel_L-BFGS-B': # pragma: no cover
if skip_parallel:
pytest.skip("Skipping parallel test.")
from qibo.tests.test_parallel import is_parallel_supported
backend_name = qibo.get_backend()
if not is_parallel_supported(backend_name):
pytest.skip("Skipping parallel test due to unsupported configuration.")
qibo.set_threads(1)
nqubits = 6
layers = 4
Expand All @@ -346,7 +344,7 @@ def test_aavqe(backend, method, options, compile, filename):
easy_hamiltonian=hamiltonians.X(nqubits)
problem_hamiltonian=hamiltonians.XXZ(nqubits)
s = lambda t: t
aavqe = models.AAVQE(circuit, easy_hamiltonian, problem_hamiltonian,
aavqe = models.AAVQE(circuit, easy_hamiltonian, problem_hamiltonian,
s, nsteps=10, t_max=1)
np.random.seed(0)
initial_parameters = np.random.uniform(0, 2*np.pi, 2*nqubits*layers + nqubits)
Expand All @@ -359,4 +357,3 @@ def test_aavqe(backend, method, options, compile, filename):
if filename is not None:
assert_regression_fixture(params, filename, rtol=1e-2)
qibo.set_threads(original_threads)

30 changes: 22 additions & 8 deletions src/qibo/tests/test_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@
from qibo.parallel import parallel_parametrized_execution, parallel_execution


def test_parallel_circuit_evaluation(backend):
def is_parallel_supported(backend_name): # pragma: no cover
if "GPU" in qibo.get_device():
return False
if backend_name in ("tensorflow", "qibojit"):
return False
if sys.platform in ("darwin", "win32"):
return False
return True


def test_parallel_circuit_evaluation(backend, skip_parallel): # pragma: no cover
"""Evaluate circuit for multiple input states."""
device = qibo.get_device()
backend = qibo.get_backend()
if 'GPU' in qibo.get_device() or sys.platform == "win32" or sys.platform == "darwin" or backend == "tensorflow" or backend == "qibojit": # pragma: no cover
pytest.skip("unsupported configuration")
backend_name = qibo.get_backend()
if skip_parallel:
pytest.skip("Skipping parallel test.")
if not is_parallel_supported(backend_name):
pytest.skip("Skipping parallel test due to unsupported configuration.")
original_threads = qibo.get_threads()
qibo.set_threads(1)

Expand All @@ -34,12 +46,14 @@ def test_parallel_circuit_evaluation(backend):
qibo.set_threads(original_threads)


def test_parallel_parametrized_circuit(backend):
def test_parallel_parametrized_circuit(backend, skip_parallel): # pragma: no cover
"""Evaluate circuit for multiple parameters."""
device = qibo.get_device()
backend = qibo.get_backend()
if 'GPU' in qibo.get_device() or sys.platform == "win32" or sys.platform == "darwin" or backend == "tensorflow" or backend == "qibojit": # pragma: no cover
pytest.skip("unsupported configuration")
backend_name = qibo.get_backend()
if skip_parallel:
pytest.skip("Skipping parallel test.")
if not is_parallel_supported(backend_name):
pytest.skip("Skipping parallel test due to unsupported configuration.")
original_threads = qibo.get_threads()
qibo.set_threads(1)

Expand Down

0 comments on commit 790c18e

Please sign in to comment.