From 8323c54ad717931392905a38133a7c8c7b80ba8f Mon Sep 17 00:00:00 2001 From: "Guan-Ming (Wesley) Chiu" <105915352+guan404ming@users.noreply.github.com> Date: Wed, 29 Oct 2025 15:09:42 +0800 Subject: [PATCH] Refactor testing structure to improve maintainability --- testing/test_create_circuit.py | 5 +- testing/test_final_quantum_states.py | 20 ++++-- testing/test_swap_test.py | 67 ++++++++++++++++++-- testing/utils/__init__.py | 20 ++++++ testing/{ => utils}/amazon_braket_helpers.py | 0 testing/{ => utils}/cirq_helpers.py | 0 testing/{conftest.py => utils/constants.py} | 16 +---- testing/{ => utils}/qiskit_helpers.py | 0 testing/{ => utils}/qumat_helpers.py | 0 9 files changed, 97 insertions(+), 31 deletions(-) create mode 100644 testing/utils/__init__.py rename testing/{ => utils}/amazon_braket_helpers.py (100%) rename testing/{ => utils}/cirq_helpers.py (100%) rename testing/{conftest.py => utils/constants.py} (68%) rename testing/{ => utils}/qiskit_helpers.py (100%) rename testing/{ => utils}/qumat_helpers.py (100%) diff --git a/testing/test_create_circuit.py b/testing/test_create_circuit.py index 8fb1d43446..48f0f3099d 100644 --- a/testing/test_create_circuit.py +++ b/testing/test_create_circuit.py @@ -17,10 +17,11 @@ import pytest -from .conftest import TESTING_BACKENDS +from .utils import TESTING_BACKENDS from qumat import QuMat +@pytest.mark.parametrize("backend_name", TESTING_BACKENDS) class TestCreateCircuit: """Test class for create_empty_circuit functionality.""" @@ -51,7 +52,6 @@ def get_backend_config(self, backend_name): }, } - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) def test_create_empty_circuit(self, backend_name): """Test that create_empty_circuit works""" backend_config = self.get_backend_config(backend_name) @@ -60,7 +60,6 @@ def test_create_empty_circuit(self, backend_name): assert qumat.circuit is not None - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) @pytest.mark.parametrize("num_qubits", [1, 3, 5]) def test_create_circuit_initializes_to_zero(self, backend_name, num_qubits): """Test that create_empty_circuit properly initializes all qubits to |0⟩.""" diff --git a/testing/test_final_quantum_states.py b/testing/test_final_quantum_states.py index b0b0e59e11..87398bdcfa 100644 --- a/testing/test_final_quantum_states.py +++ b/testing/test_final_quantum_states.py @@ -19,19 +19,21 @@ import numpy as np from importlib import import_module -from .conftest import TESTING_BACKENDS -from .qumat_helpers import get_qumat_example_final_state_vector +from .utils import TESTING_BACKENDS +from .utils.qumat_helpers import get_qumat_example_final_state_vector +@pytest.mark.parametrize("backend_name", TESTING_BACKENDS) class TestFinalQuantumStates: """Test class for final quantum state comparisons between QuMat and native implementations.""" - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) @pytest.mark.parametrize("initial_ket_str", ["000", "001", "010", "011"]) def test_backend_final_state_vector(self, backend_name, initial_ket_str): """Test that QuMat produces same final state as native backend implementation.""" # Import backend-specific helpers - backend_module = import_module(f".{backend_name}_helpers", package="testing") + backend_module = import_module( + f".{backend_name}_helpers", package="testing.utils" + ) # Get native implementation result native_example_vector = backend_module.get_native_example_final_state_vector( @@ -53,14 +55,18 @@ def test_backend_final_state_vector(self, backend_name, initial_ket_str): err_msg=f"State vectors don't match for initial state {initial_ket_str} using {backend_name}", ) - def test_all_backends_consistency(self, testing_backends): + +class TestFinalQuantumStatesConsistency: + """Test class for consistency checks across all backends.""" + + def test_all_backends_consistency(self): """Test that all available backends produce consistent results.""" initial_ket_str = "001" results = {} - for backend_name in testing_backends: + for backend_name in TESTING_BACKENDS: backend_module = import_module( - f".{backend_name}_helpers", package="testing" + f".{backend_name}_helpers", package="testing.utils" ) qumat_backend_config = backend_module.get_qumat_backend_config( "get_final_state_vector" diff --git a/testing/test_swap_test.py b/testing/test_swap_test.py index 2b5c9b9f6f..02d7f884e1 100644 --- a/testing/test_swap_test.py +++ b/testing/test_swap_test.py @@ -17,10 +17,11 @@ import pytest -from .conftest import TESTING_BACKENDS +from .utils import TESTING_BACKENDS from qumat import QuMat +@pytest.mark.parametrize("backend_name", TESTING_BACKENDS) class TestSwapTest: """Test class for swap test functionality across different backends.""" @@ -76,7 +77,6 @@ def calculate_prob_zero(self, results, backend_name): prob_zero = count_zero / total_shots return prob_zero - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) def test_identical_zero_states(self, backend_name): """Test swap test with two identical |0> states.""" backend_config = self.get_backend_config(backend_name) @@ -97,7 +97,6 @@ def test_identical_zero_states(self, backend_name): prob_zero = self.calculate_prob_zero(results, backend_name) assert prob_zero > 0.95, f"Expected P(0) ≈ 1.0, got {prob_zero}" - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) def test_orthogonal_states(self, backend_name): """Test swap test with orthogonal states |0> and |1>.""" backend_config = self.get_backend_config(backend_name) @@ -120,7 +119,6 @@ def test_orthogonal_states(self, backend_name): prob_zero = self.calculate_prob_zero(results, backend_name) assert 0.45 < prob_zero < 0.55, f"Expected P(0) ≈ 0.5, got {prob_zero}" - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) def test_identical_one_states(self, backend_name): """Test swap test with two identical |1> states. @@ -150,7 +148,6 @@ def test_identical_one_states(self, backend_name): f"Expected P(0) ≈ 0 or ≈ 1 for identical states, got {prob_zero}" ) - @pytest.mark.parametrize("backend_name", TESTING_BACKENDS) def test_cswap_gate_exists(self, backend_name): """Test that the CSWAP gate is properly implemented.""" backend_config = self.get_backend_config(backend_name) @@ -165,11 +162,67 @@ def test_cswap_gate_exists(self, backend_name): except Exception as e: pytest.fail(f"CSWAP gate failed on {backend_name}: {str(e)}") - def test_all_backends_consistency(self, testing_backends): + +class TestSwapTestConsistency: + """Test class for consistency checks across all backends.""" + + def get_backend_config(self, backend_name): + """Helper method to get backend configuration.""" + if backend_name == "qiskit": + return { + "backend_name": backend_name, + "backend_options": { + "simulator_type": "aer_simulator", + "shots": 10000, + }, + } + elif backend_name == "cirq": + return { + "backend_name": backend_name, + "backend_options": { + "simulator_type": "default", + "shots": 10000, + }, + } + elif backend_name == "amazon_braket": + return { + "backend_name": backend_name, + "backend_options": { + "simulator_type": "local", + "shots": 10000, + }, + } + + def calculate_prob_zero(self, results, backend_name): + """Calculate probability of measuring ancilla qubit in |0> state.""" + if isinstance(results, list): + results = results[0] + + total_shots = sum(results.values()) + + # Count measurements where ancilla (qubit 0) is in |0> state + # Different backends return different formats: + # - Cirq: integer keys (e.g., 0, 1, 2, 3 for 3-qubit system) + # - Qiskit/Braket: string keys (e.g., '000', '001', '010', '011') + count_zero = 0 + for state, count in results.items(): + if isinstance(state, str): + # For string format, check the rightmost bit (ancilla is qubit 0) + if state[-1] == "0": + count_zero += count + else: + # For integer format, check if least significant bit is 0 + if (state & 1) == 0: + count_zero += count + + prob_zero = count_zero / total_shots + return prob_zero + + def test_all_backends_consistency(self): """Test that all backends produce consistent results for the same swap test.""" results_dict = {} - for backend_name in testing_backends: + for backend_name in TESTING_BACKENDS: backend_config = self.get_backend_config(backend_name) qumat = QuMat(backend_config) diff --git a/testing/utils/__init__.py b/testing/utils/__init__.py new file mode 100644 index 0000000000..8c752e381e --- /dev/null +++ b/testing/utils/__init__.py @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from .constants import TESTING_BACKENDS + +__all__ = ["TESTING_BACKENDS"] diff --git a/testing/amazon_braket_helpers.py b/testing/utils/amazon_braket_helpers.py similarity index 100% rename from testing/amazon_braket_helpers.py rename to testing/utils/amazon_braket_helpers.py diff --git a/testing/cirq_helpers.py b/testing/utils/cirq_helpers.py similarity index 100% rename from testing/cirq_helpers.py rename to testing/utils/cirq_helpers.py diff --git a/testing/conftest.py b/testing/utils/constants.py similarity index 68% rename from testing/conftest.py rename to testing/utils/constants.py index 5f203a2af7..372d1f2247 100644 --- a/testing/conftest.py +++ b/testing/utils/constants.py @@ -15,19 +15,7 @@ # limitations under the License. # -import pytest -import sys -from pathlib import Path +"""Constants for testing.""" -# Add project root to Python path -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -# Define backends to test - used by both parametrize and fixture +# List of backends to test across TESTING_BACKENDS = ["qiskit", "cirq", "amazon_braket"] - - -@pytest.fixture(scope="session") -def testing_backends(): - """Fixture to provide the list of backends to test.""" - return TESTING_BACKENDS diff --git a/testing/qiskit_helpers.py b/testing/utils/qiskit_helpers.py similarity index 100% rename from testing/qiskit_helpers.py rename to testing/utils/qiskit_helpers.py diff --git a/testing/qumat_helpers.py b/testing/utils/qumat_helpers.py similarity index 100% rename from testing/qumat_helpers.py rename to testing/utils/qumat_helpers.py