From 29ca5044e949f461266e4b9cfb2861cb6871591f Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 30 Jan 2025 11:35:02 +0100 Subject: [PATCH] test: add conftest and first observable test --- tests/conftest.py | 66 +++++++++++++++++++++++++++++++++++++++ tests/test_observables.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tests/conftest.py create mode 100644 tests/test_observables.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..0a18bfa --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,66 @@ +"""conftest.py. + +Pytest fixtures. +""" + +import sys + +import pytest + +# backends to be tested +# TODO: add cutensornet and quimb here as well +BACKENDS = ["qmatchatea"] + + +def get_backend(backend_name): + + from qibotn.backends.qmatchatea import QMatchaTeaBackend + + NAME2BACKEND = { + "qmatchatea": QMatchaTeaBackend, + } + + return NAME2BACKEND[backend_name]() + + +AVAILABLE_BACKENDS = [] +for backend_name in BACKENDS: + try: + _backend = get_backend(backend_name) + AVAILABLE_BACKENDS.append(backend_name) + except (ModuleNotFoundError, ImportError): + pass + + +def pytest_runtest_setup(item): + ALL = {"darwin", "linux"} + supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers()) + plat = sys.platform + if supported_platforms and plat not in supported_platforms: # pragma: no cover + # case not covered by workflows + pytest.skip(f"Cannot run test on platform {plat}.") + + +@pytest.fixture +def backend(backend_name): + yield get_backend(backend_name) + + +def pytest_runtest_setup(item): + ALL = {"darwin", "linux"} + supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers()) + plat = sys.platform + if supported_platforms and plat not in supported_platforms: # pragma: no cover + # case not covered by workflows + pytest.skip(f"Cannot run test on platform {plat}.") + + +def pytest_configure(config): + config.addinivalue_line("markers", "linux: mark test to run only on linux") + + +def pytest_generate_tests(metafunc): + module_name = metafunc.module.__name__ + + if "backend_name" in metafunc.fixturenames: + metafunc.parametrize("backend_name", AVAILABLE_BACKENDS) diff --git a/tests/test_observables.py b/tests/test_observables.py new file mode 100644 index 0000000..d89c6f9 --- /dev/null +++ b/tests/test_observables.py @@ -0,0 +1,47 @@ +import math +import random + +import pytest +from qibo import Circuit, construct_backend, gates, hamiltonians +from qibo.symbols import X, Z + + +def build_observable(nqubits): + """Helper function to construct a target observable.""" + hamiltonian_form = 0 + for i in range(nqubits): + hamiltonian_form += 0.5 * X(i % nqubits) * Z((i + 1) % nqubits) + + hamiltonian = hamiltonians.SymbolicHamiltonian(form=hamiltonian_form) + return hamiltonian, hamiltonian_form + + +def build_circuit(nqubits, nlayers, seed=42): + """Helper function to construct a layered quantum circuit.""" + random.seed(seed) + + circ = Circuit(nqubits) + for _ in range(nlayers): + for q in range(nqubits): + circ.add(gates.RY(q=q, theta=random.uniform(-math.pi, math.pi))) + circ.add(gates.RZ(q=q, theta=random.uniform(-math.pi, math.pi))) + [circ.add(gates.CNOT(q % nqubits, (q + 1) % nqubits) for q in range(nqubits))] + circ.add(gates.M(*range(nqubits))) + return circ + + +@pytest.mark.parametrize("nqubits", [2, 5, 10]) +def test_observable_expval(backend, nqubits): + numpy_backend = construct_backend("numpy") + ham, ham_form = build_observable(nqubits) + circ = build_circuit(nqubits=nqubits, nlayers=1) + + exact_expval = numpy_backend.calculate_expectation_state( + hamiltonian=ham, + state=circ().state(), + normalize=False, + ) + + tn_expval = backend.expectation(circuit=circ, observable=ham_form) + + assert math.isclose(exact_expval, tn_expval, abs_tol=1e-7)