Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 2 additions & 3 deletions testing/test_create_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down Expand Up @@ -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)
Expand All @@ -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⟩."""
Expand Down
20 changes: 13 additions & 7 deletions testing/test_final_quantum_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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"
Expand Down
67 changes: 60 additions & 7 deletions testing/test_swap_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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.

Expand Down Expand Up @@ -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)
Expand All @@ -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)

Expand Down
20 changes: 20 additions & 0 deletions testing/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -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"]
File renamed without changes.
16 changes: 2 additions & 14 deletions testing/conftest.py → testing/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
File renamed without changes.
File renamed without changes.