Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test all available platforms automatically #539

Merged
merged 17 commits into from
Feb 2, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rules.yml
Original file line number Diff line number Diff line change
@@ -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: |
2 changes: 1 addition & 1 deletion src/qibo/core/gates.py
Original file line number Diff line number Diff line change
@@ -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:
2 changes: 1 addition & 1 deletion src/qibo/optimizers.py
Original file line number Diff line number Diff line change
@@ -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,
8 changes: 4 additions & 4 deletions src/qibo/parallel.py
Original file line number Diff line number Diff line change
@@ -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:
@@ -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")
@@ -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
93 changes: 63 additions & 30 deletions src/qibo/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -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)


@@ -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):
@@ -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",
@@ -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
@@ -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)
6 changes: 3 additions & 3 deletions src/qibo/tests/test_core_distcircuit_execution.py
Original file line number Diff line number Diff line change
@@ -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):
@@ -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):
@@ -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):
2 changes: 1 addition & 1 deletion src/qibo/tests/test_core_fusion.py
Original file line number Diff line number Diff line change
@@ -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):
10 changes: 6 additions & 4 deletions src/qibo/tests/test_models_qgan.py
Original file line number Diff line number Diff line change
@@ -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):
37 changes: 17 additions & 20 deletions src/qibo/tests/test_models_variational.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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)

@@ -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)