From f420a9403681e2470cfcc87aac1a47c525ebd6f3 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 17:19:39 +0100 Subject: [PATCH] test: circuit execution --- tests/test_circuit_execution.py | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/test_circuit_execution.py diff --git a/tests/test_circuit_execution.py b/tests/test_circuit_execution.py new file mode 100644 index 0000000..4ee396e --- /dev/null +++ b/tests/test_circuit_execution.py @@ -0,0 +1,92 @@ +import math + +import pytest +from qibo import Circuit, gates, hamiltonians +from qibo.symbols import X, Z + +from qibotn.backends.qmatchatea import QMatchaTeaBackend + + +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_GHZ(nqubits): + """Helper function to construct a layered quantum circuit.""" + circ = Circuit(nqubits) + circ.add(gates.H(0)) + [circ.add(gates.CNOT(q, q + 1)) for q in range(nqubits - 1)] + return circ + + +def construct_targets(nqubits): + """Construct strings of 1s and 0s of size `nqubits`.""" + ones = "1" * nqubits + zeros = "0" * nqubits + return ones, zeros + + +@pytest.mark.parametrize("nqubits", [2, 10, 40]) +def test_probabilities(backend, nqubits): + + circ = build_GHZ(nqubits=nqubits) + ones, zeros = construct_targets(nqubits) + + if isinstance(backend, QMatchaTeaBackend): + # unbiased prob + out_u = backend.execute_circuit( + circuit=circ, + prob_type="U", + num_samples=1000, + ).probabilities() + + math.isclose(out_u[ones], 0.5, abs_tol=1e-7) + math.isclose(out_u[zeros], 0.5, abs_tol=1e-7) + + out_g = backend.execute_circuit( + circuit=circ, + prob_type="G", + prob_threshold=1.0, + ).probabilities() + + math.isclose(out_g[ones], 0.5, abs_tol=1e-7) + math.isclose(out_g[zeros], 0.5, abs_tol=1e-7) + + out_e = backend.execute_circuit( + circuit=circ, + prob_type="E", + prob_threshold=0.2, + ).probabilities() + + math.isclose(out_e[ones], 0.5, abs_tol=1e-7) + math.isclose(out_e[zeros], 0.5, abs_tol=1e-7) + + +@pytest.mark.parametrize("nqubits", [2, 10, 40]) +@pytest.mark.parametrize("nshots", [100, 1000]) +def test_shots(backend, nqubits, nshots): + circ = build_GHZ(nqubits=nqubits) + ones, zeros = construct_targets(nqubits) + + # For p = 0.5, sigma = sqrt(nshots * 0.5 * 0.5) = sqrt(nshots)/2. + sigma_threshold = 3 * (math.sqrt(nshots) / 2) + + outcome = backend.execute_circuit(circ, nshots=nshots) + frequencies = outcome.frequencies() + + shots_ones = frequencies.get(ones, 0) + shots_zeros = frequencies.get(zeros, 0) + + # Check that the counts for both outcomes are within the 3-sigma threshold of nshots/2. + assert ( + abs(shots_ones - (nshots / 2)) < sigma_threshold + ), f"Count for {ones} deviates too much: {shots_ones} vs expected {nshots/2}" + assert ( + abs(shots_zeros - (nshots / 2)) < sigma_threshold + ), f"Count for {zeros} deviates too much: {shots_zeros} vs expected {nshots/2}"