This is an automated email from the ASF dual-hosted git repository.

guanmingchiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/mahout.git


The following commit(s) were added to refs/heads/main by this push:
     new f210fa43f MAHOUT-604: add test_multi_qubit_gates.py - part 1 (#629)
f210fa43f is described below

commit f210fa43f572e6104ee07b2506acacb9b387e606
Author: GUAN-HAO HUANG <[email protected]>
AuthorDate: Wed Nov 26 10:38:30 2025 +0800

    MAHOUT-604: add test_multi_qubit_gates.py - part 1 (#629)
    
    * MAHOUT-604: add test_multi_qubit_gates.py - part 1
    
    * follow the DRY and fix lint
    
    * DRY
    
    ---------
    
    Co-authored-by: rich <[email protected]>
---
 testing/test_multi_qubit_gates.py  | 492 +++++++++++++++++++++++++++++++++++++
 testing/test_single_qubit_gates.py |  90 +++----
 testing/utils/__init__.py          |   4 +-
 testing/utils/qumat_helpers.py     |  67 +++++
 4 files changed, 596 insertions(+), 57 deletions(-)

diff --git a/testing/test_multi_qubit_gates.py 
b/testing/test_multi_qubit_gates.py
new file mode 100644
index 000000000..9c5febbd6
--- /dev/null
+++ b/testing/test_multi_qubit_gates.py
@@ -0,0 +1,492 @@
+#
+# 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.
+#
+
+import pytest
+
+from .utils import TESTING_BACKENDS, get_backend_config, get_state_probability
+from qumat import QuMat
+
+
+def create_qumat_instance(backend_name, num_qubits):
+    """Create and initialize a QuMat instance with a circuit."""
+    backend_config = get_backend_config(backend_name)
+    qumat = QuMat(backend_config)
+    qumat.create_empty_circuit(num_qubits=num_qubits)
+    return qumat
+
+
+def prepare_initial_state(qumat, initial_state_str):
+    """
+    Prepare initial state by applying X gates to qubits that should be |1⟩.
+
+    Args:
+        qumat: QuMat instance
+        initial_state_str: Binary string representing initial state (e.g., 
"101")
+    """
+    for i, bit in enumerate(initial_state_str):
+        if bit == "1":
+            qumat.apply_pauli_x_gate(i)
+
+
+def execute_and_assert_state(
+    qumat, expected_state, num_qubits, backend_name, threshold=0.95, 
context_msg=""
+):
+    """
+    Execute circuit and assert expected state probability.
+
+    Args:
+        qumat: QuMat instance
+        expected_state: Expected state string or int
+        num_qubits: Number of qubits
+        backend_name: Backend name
+        threshold: Probability threshold (default 0.95)
+        context_msg: Additional context message for assertion
+    """
+    results = qumat.execute_circuit()
+    prob = get_state_probability(results, expected_state, num_qubits, 
backend_name)
+    assert prob > threshold, (
+        f"Backend: {backend_name}, {context_msg}, "
+        f"Expected: |{expected_state}⟩, Got probability: {prob:.4f}"
+    )
+    return prob
+
+
[email protected]("backend_name", TESTING_BACKENDS)
+class TestCNOTGate:
+    """Test class for CNOT gate functionality."""
+
+    @pytest.mark.parametrize(
+        "initial_state, control_qubit, target_qubit, expected_state",
+        [
+            ("00", 0, 1, "00"),  # |00⟩ -> CNOT(0,1) -> |00⟩ (control=0, no 
flip)
+            ("01", 0, 1, "01"),  # |01⟩ -> CNOT(0,1) -> |01⟩ (control=0, no 
flip)
+            ("10", 0, 1, "11"),  # |10⟩ -> CNOT(0,1) -> |11⟩ (control=1, flip 
target)
+            ("11", 0, 1, "10"),  # |11⟩ -> CNOT(0,1) -> |10⟩ (control=1, flip 
target)
+            ("00", 1, 0, "00"),  # |00⟩ -> CNOT(1,0) -> |00⟩ (control=0, no 
flip)
+            ("01", 1, 0, "11"),  # |01⟩ -> CNOT(1,0) -> |11⟩ (control=1, flip 
target)
+            ("10", 1, 0, "10"),  # |10⟩ -> CNOT(1,0) -> |10⟩ (control=0, no 
flip)
+            ("11", 1, 0, "01"),  # |11⟩ -> CNOT(1,0) -> |01⟩ (control=1, flip 
target)
+        ],
+    )
+    def test_cnot_state_transitions(
+        self, backend_name, initial_state, control_qubit, target_qubit, 
expected_state
+    ):
+        """Test CNOT gate state transitions with parametrized test cases."""
+        qumat = create_qumat_instance(backend_name, num_qubits=2)
+        prepare_initial_state(qumat, initial_state)
+        qumat.apply_cnot_gate(control_qubit, target_qubit)
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=2,
+            backend_name=backend_name,
+            context_msg=(
+                f"Initial state: |{initial_state}⟩, "
+                f"CNOT(control={control_qubit}, target={target_qubit})"
+            ),
+        )
+
+    @pytest.mark.parametrize(
+        "initial_state, num_applications, expected_state",
+        [
+            ("00", 1, "00"),  # |00⟩ -> CNOT -> |00⟩
+            ("00", 2, "00"),  # |00⟩ -> CNOT -> CNOT -> |00⟩ (CNOT² = I)
+            ("10", 1, "11"),  # |10⟩ -> CNOT -> |11⟩
+            ("10", 2, "10"),  # |10⟩ -> CNOT -> |11⟩ -> CNOT -> |10⟩ (CNOT² = 
I)
+            ("11", 1, "10"),  # |11⟩ -> CNOT -> |10⟩
+            ("11", 2, "11"),  # |11⟩ -> CNOT -> |10⟩ -> CNOT -> |11⟩ (CNOT² = 
I)
+        ],
+    )
+    def test_cnot_double_application(
+        self, backend_name, initial_state, num_applications, expected_state
+    ):
+        """Test that applying CNOT twice returns to original state (CNOT² = 
I)."""
+        qumat = create_qumat_instance(backend_name, num_qubits=2)
+        prepare_initial_state(qumat, initial_state)
+        for _ in range(num_applications):
+            qumat.apply_cnot_gate(0, 1)
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=2,
+            backend_name=backend_name,
+            context_msg=(
+                f"Initial state: |{initial_state}⟩, "
+                f"CNOT applications: {num_applications}"
+            ),
+        )
+
+    @pytest.mark.parametrize(
+        "control_qubit, target_qubit, num_qubits",
+        [
+            # 3-qubit circuits
+            (0, 1, 3),  # CNOT on qubits 0 and 1 in 3-qubit circuit
+            (1, 2, 3),  # CNOT on qubits 1 and 2 in 3-qubit circuit
+            (0, 2, 3),  # CNOT on qubits 0 and 2 in 3-qubit circuit
+            # 4-qubit circuits
+            (0, 1, 4),  # CNOT on qubits 0 and 1 in 4-qubit circuit
+            (0, 3, 4),  # CNOT on qubits 0 and 3 in 4-qubit circuit
+            (1, 2, 4),  # CNOT on qubits 1 and 2 in 4-qubit circuit
+            (2, 3, 4),  # CNOT on qubits 2 and 3 in 4-qubit circuit
+            # 5-qubit circuits
+            (0, 4, 5),  # CNOT on qubits 0 and 4 in 5-qubit circuit
+            (2, 3, 5),  # CNOT on qubits 2 and 3 in 5-qubit circuit
+        ],
+    )
+    def test_cnot_on_multiple_qubits(
+        self, backend_name, control_qubit, target_qubit, num_qubits
+    ):
+        """Test CNOT gate on different qubit pairs in multi-qubit circuits."""
+        qumat = create_qumat_instance(backend_name, num_qubits=num_qubits)
+        qumat.apply_pauli_x_gate(control_qubit)
+        qumat.apply_cnot_gate(control_qubit, target_qubit)
+        # Expected: control and target qubits should both be |1⟩
+        expected_state = "".join(
+            "1" if i in (control_qubit, target_qubit) else "0"
+            for i in range(num_qubits)
+        )
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=num_qubits,
+            backend_name=backend_name,
+            context_msg=(
+                f"CNOT(control={control_qubit}, target={target_qubit}) "
+                f"on {num_qubits}-qubit circuit"
+            ),
+        )
+
+    @pytest.mark.parametrize(
+        "control_qubit, target_qubit, expected_states",
+        [
+            # Standard Bell state: |00⟩ + |11⟩
+            (0, 1, ["00", "11"]),
+            # Reversed control/target: |00⟩ + |11⟩ (same result)
+            (1, 0, ["00", "11"]),
+        ],
+    )
+    def test_cnot_entanglement(
+        self, backend_name, control_qubit, target_qubit, expected_states
+    ):
+        """Test that CNOT gate creates entanglement (Bell state) with 
parametrized test cases."""
+        qumat = create_qumat_instance(backend_name, num_qubits=2)
+        qumat.apply_hadamard_gate(control_qubit)
+        qumat.apply_cnot_gate(control_qubit, target_qubit)
+        results = qumat.execute_circuit()
+        # Should measure expected states with approximately equal probability
+        for expected_state in expected_states:
+            prob = get_state_probability(
+                results, expected_state, num_qubits=2, 
backend_name=backend_name
+            )
+            assert 0.45 < prob < 0.55, (
+                f"Backend: {backend_name}, "
+                f"CNOT(control={control_qubit}, target={target_qubit}), "
+                f"Expected ~0.5 probability for |{expected_state}⟩ in Bell 
state, got {prob:.4f}"
+            )
+
+
[email protected]("backend_name", TESTING_BACKENDS)
+class TestToffoliGate:
+    """Test class for Toffoli gate functionality."""
+
+    @pytest.mark.parametrize(
+        "initial_state, control1, control2, target, expected_state",
+        [
+            # Toffoli(0,1,2): flip target only if both controls are |1⟩
+            ("000", 0, 1, 2, "000"),  # |000⟩ -> Toffoli -> |000⟩
+            ("001", 0, 1, 2, "001"),  # |001⟩ -> Toffoli -> |001⟩
+            ("010", 0, 1, 2, "010"),  # |010⟩ -> Toffoli -> |010⟩
+            ("011", 0, 1, 2, "011"),  # |011⟩ -> Toffoli -> |011⟩
+            ("100", 0, 1, 2, "100"),  # |100⟩ -> Toffoli -> |100⟩
+            ("101", 0, 1, 2, "101"),  # |101⟩ -> Toffoli -> |101⟩
+            (
+                "110",
+                0,
+                1,
+                2,
+                "111",
+            ),  # |110⟩ -> Toffoli -> |111⟩ (both controls=1, flip target)
+            (
+                "111",
+                0,
+                1,
+                2,
+                "110",
+            ),  # |111⟩ -> Toffoli -> |110⟩ (both controls=1, flip target)
+            # Different control/target combinations
+            # |110⟩ -> Toffoli(0,2,1): control0=1, control2=0 -> no flip, 
result |110⟩
+            (
+                "110",
+                0,
+                2,
+                1,
+                "110",
+            ),  # |110⟩ -> Toffoli(0,2,1) -> |110⟩ (control2=0, no flip)
+            # |101⟩ -> Toffoli(1,2,0): control1=0, control2=1 -> no flip, 
result |101⟩
+            (
+                "101",
+                1,
+                2,
+                0,
+                "101",
+            ),  # |101⟩ -> Toffoli(1,2,0) -> |101⟩ (control1=0, no flip)
+            # Test cases where both controls are 1 with different target
+            (
+                "111",
+                0,
+                2,
+                1,
+                "101",
+            ),  # |111⟩ -> Toffoli(0,2,1) -> |101⟩ (both controls=1, flip 
target)
+            (
+                "111",
+                1,
+                2,
+                0,
+                "011",
+            ),  # |111⟩ -> Toffoli(1,2,0) -> |011⟩ (both controls=1, flip 
target)
+        ],
+    )
+    def test_toffoli_state_transitions(
+        self,
+        backend_name,
+        initial_state,
+        control1,
+        control2,
+        target,
+        expected_state,
+    ):
+        """Test Toffoli gate state transitions with parametrized test cases."""
+        qumat = create_qumat_instance(backend_name, num_qubits=3)
+        prepare_initial_state(qumat, initial_state)
+        qumat.apply_toffoli_gate(control1, control2, target)
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=3,
+            backend_name=backend_name,
+            context_msg=(
+                f"Initial state: |{initial_state}⟩, "
+                f"Toffoli(control1={control1}, control2={control2}, 
target={target})"
+            ),
+        )
+
+    @pytest.mark.parametrize(
+        "initial_state, num_applications, expected_state",
+        [
+            ("000", 1, "000"),  # |000⟩ -> Toffoli -> |000⟩
+            ("000", 2, "000"),  # |000⟩ -> Toffoli -> Toffoli -> |000⟩ 
(Toffoli² = I)
+            ("110", 1, "111"),  # |110⟩ -> Toffoli -> |111⟩
+            (
+                "110",
+                2,
+                "110",
+            ),  # |110⟩ -> Toffoli -> |111⟩ -> Toffoli -> |110⟩ (Toffoli² = I)
+            ("111", 1, "110"),  # |111⟩ -> Toffoli -> |110⟩
+            (
+                "111",
+                2,
+                "111",
+            ),  # |111⟩ -> Toffoli -> |110⟩ -> Toffoli -> |111⟩ (Toffoli² = I)
+        ],
+    )
+    def test_toffoli_double_application(
+        self, backend_name, initial_state, num_applications, expected_state
+    ):
+        """Test that applying Toffoli twice returns to original state 
(Toffoli² = I)."""
+        qumat = create_qumat_instance(backend_name, num_qubits=3)
+        prepare_initial_state(qumat, initial_state)
+        for _ in range(num_applications):
+            qumat.apply_toffoli_gate(0, 1, 2)
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=3,
+            backend_name=backend_name,
+            context_msg=(
+                f"Initial state: |{initial_state}⟩, "
+                f"Toffoli applications: {num_applications}"
+            ),
+        )
+
+    @pytest.mark.parametrize(
+        "control1, control2, target, num_qubits",
+        [
+            # 4-qubit circuits
+            (0, 1, 2, 4),  # Toffoli on qubits 0, 1, 2 in 4-qubit circuit
+            (0, 1, 3, 4),  # Toffoli on qubits 0, 1, 3 in 4-qubit circuit
+            (1, 2, 3, 4),  # Toffoli on qubits 1, 2, 3 in 4-qubit circuit
+            (0, 2, 3, 4),  # Toffoli on qubits 0, 2, 3 in 4-qubit circuit
+            # 5-qubit circuits
+            (0, 1, 2, 5),  # Toffoli on qubits 0, 1, 2 in 5-qubit circuit
+            (0, 1, 4, 5),  # Toffoli on qubits 0, 1, 4 in 5-qubit circuit
+            (2, 3, 4, 5),  # Toffoli on qubits 2, 3, 4 in 5-qubit circuit
+        ],
+    )
+    def test_toffoli_on_multiple_qubits(
+        self, backend_name, control1, control2, target, num_qubits
+    ):
+        """Test Toffoli gate on different qubit combinations in multi-qubit 
circuits."""
+        qumat = create_qumat_instance(backend_name, num_qubits=num_qubits)
+        qumat.apply_pauli_x_gate(control1)
+        qumat.apply_pauli_x_gate(control2)
+        qumat.apply_toffoli_gate(control1, control2, target)
+        # Expected: all three qubits should be |1⟩
+        expected_state = "".join(
+            "1" if i in (control1, control2, target) else "0" for i in 
range(num_qubits)
+        )
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=num_qubits,
+            backend_name=backend_name,
+            context_msg=(
+                f"Toffoli(control1={control1}, control2={control2}, 
target={target}) "
+                f"on {num_qubits}-qubit circuit"
+            ),
+        )
+
+    @pytest.mark.parametrize(
+        "initial_state, expected_state, control1, control2, target",
+        [
+            # Toffoli(0,1,2): target = control0 AND control1
+            ("000", "000", 0, 1, 2),  # 0 AND 0 = 0
+            ("010", "010", 0, 1, 2),  # 0 AND 1 = 0
+            ("100", "100", 0, 1, 2),  # 1 AND 0 = 0
+            ("110", "111", 0, 1, 2),  # 1 AND 1 = 1 (target flips)
+            # Toffoli(0,2,1): target = control0 AND control2
+            ("000", "000", 0, 2, 1),  # 0 AND 0 = 0
+            ("001", "001", 0, 2, 1),  # 0 AND 1 = 0
+            ("100", "100", 0, 2, 1),  # 1 AND 0 = 0
+            ("101", "111", 0, 2, 1),  # 1 AND 1 = 1 (target flips)
+            # Toffoli(1,2,0): target = control1 AND control2
+            ("000", "000", 1, 2, 0),  # 0 AND 0 = 0
+            ("001", "001", 1, 2, 0),  # 0 AND 1 = 0
+            ("010", "010", 1, 2, 0),  # 1 AND 0 = 0
+            ("011", "111", 1, 2, 0),  # 1 AND 1 = 1 (target flips)
+        ],
+    )
+    def test_toffoli_and_gate_behavior(
+        self, backend_name, initial_state, expected_state, control1, control2, 
target
+    ):
+        """Test that Toffoli gate acts as a quantum AND gate with parametrized 
test cases."""
+        qumat = create_qumat_instance(backend_name, num_qubits=3)
+        prepare_initial_state(qumat, initial_state)
+        qumat.apply_toffoli_gate(control1, control2, target)
+        execute_and_assert_state(
+            qumat,
+            expected_state,
+            num_qubits=3,
+            backend_name=backend_name,
+            context_msg=(
+                f"Toffoli AND gate: |{initial_state}⟩ -> "
+                f"Toffoli({control1},{control2},{target}) -> 
|{expected_state}⟩"
+            ),
+        )
+
+
[email protected]("backend_name", TESTING_BACKENDS)
+class TestMultiQubitGatesEdgeCases:
+    """Test class for edge cases of multi-qubit gates."""
+
+    @pytest.mark.parametrize(
+        "gate_name, gate_args",
+        [
+            ("cnot", (0, 1)),
+            ("toffoli", (0, 1, 2)),
+        ],
+    )
+    def test_gate_on_uninitialized_circuit(self, backend_name, gate_name, 
gate_args):
+        """Test that gates raise error on uninitialized circuit."""
+        backend_config = get_backend_config(backend_name)
+        qumat = QuMat(backend_config)
+        with pytest.raises(RuntimeError, match="circuit not initialized"):
+            if gate_name == "cnot":
+                qumat.apply_cnot_gate(*gate_args)
+            else:
+                qumat.apply_toffoli_gate(*gate_args)
+
+    @pytest.mark.parametrize(
+        "num_qubits, gate_name, gate_args",
+        [
+            (2, "cnot", (5, 6)),
+            (3, "cnot", (10, 11)),
+            (4, "cnot", (5, 6)),
+            (3, "toffoli", (5, 6, 7)),
+            (4, "toffoli", (10, 11, 12)),
+            (5, "toffoli", (6, 7, 8)),
+        ],
+    )
+    def test_gate_with_invalid_qubit_indices(
+        self, backend_name, num_qubits, gate_name, gate_args
+    ):
+        """Test that gates handle invalid qubit indices appropriately."""
+        qumat = create_qumat_instance(backend_name, num_qubits=num_qubits)
+        try:
+            if gate_name == "cnot":
+                qumat.apply_cnot_gate(*gate_args)
+            else:
+                qumat.apply_toffoli_gate(*gate_args)
+        except (IndexError, ValueError, RuntimeError, Exception):
+            pass
+
+    @pytest.mark.parametrize(
+        "num_qubits, gate_name, gate_args",
+        [
+            (2, "cnot", (0, 0)),
+            (3, "cnot", (1, 1)),
+            (4, "cnot", (2, 2)),
+            (3, "toffoli", (0, 0, 0)),
+            (4, "toffoli", (1, 1, 1)),
+            (5, "toffoli", (2, 2, 2)),
+            (3, "toffoli", (0, 1, 0)),
+            (3, "toffoli", (0, 0, 1)),
+        ],
+    )
+    def test_gate_with_same_qubits(
+        self, backend_name, num_qubits, gate_name, gate_args
+    ):
+        """Test gates with same qubits (should raise error or handle 
gracefully)."""
+        qumat = create_qumat_instance(backend_name, num_qubits=num_qubits)
+        try:
+            if gate_name == "cnot":
+                qumat.apply_cnot_gate(*gate_args)
+            else:
+                qumat.apply_toffoli_gate(*gate_args)
+            results = qumat.execute_circuit()
+            assert results is not None
+        except (ValueError, RuntimeError, Exception):
+            pass
+
+    def test_cnot_cross_backend_consistency(self, backend_name):
+        """Test that CNOT gate produces consistent results across all 
backends."""
+        results_dict = {}
+        for backend in TESTING_BACKENDS:
+            qumat = create_qumat_instance(backend, num_qubits=2)
+            qumat.apply_pauli_x_gate(0)
+            qumat.apply_cnot_gate(0, 1)
+            results = qumat.execute_circuit()
+            results_dict[backend] = get_state_probability(
+                results, "11", num_qubits=2, backend_name=backend
+            )
+        # All backends should give similar results (within 5% tolerance)
+        probabilities = list(results_dict.values())
+        for i in range(len(probabilities)):
+            for j in range(i + 1, len(probabilities)):
+                assert abs(probabilities[i] - probabilities[j]) < 0.05, (
+                    f"Backends have inconsistent CNOT results: {results_dict}"
+                )
diff --git a/testing/test_single_qubit_gates.py 
b/testing/test_single_qubit_gates.py
index 3297bce86..9b66ab4da 100644
--- a/testing/test_single_qubit_gates.py
+++ b/testing/test_single_qubit_gates.py
@@ -18,53 +18,10 @@
 import math
 import pytest
 
-from .utils import TESTING_BACKENDS, get_backend_config
+from .utils import TESTING_BACKENDS, get_backend_config, get_state_probability
 from qumat import QuMat
 
 
-def get_state_probability(results, target_state, num_qubits=1):
-    """
-    Calculate the probability of measuring a target state.
-
-    Args:
-        results: Dictionary of measurement results from execute_circuit()
-        target_state: Target state as string (e.g., "0", "1", "101") or int
-        num_qubits: Number of qubits in the circuit
-
-    Returns:
-        Probability of measuring the target state
-    """
-    if isinstance(results, list):
-        results = results[0]
-
-    total_shots = sum(results.values())
-    if total_shots == 0:
-        return 0.0
-
-    # Convert target_state to both string and int formats for comparison
-    if isinstance(target_state, str):
-        target_str = target_state
-        # Convert binary string to integer
-        target_int = int(target_state, 2) if target_state else 0
-    else:
-        target_int = target_state
-        # Convert integer to binary string
-        target_str = format(target_state, f"0{num_qubits}b")
-
-    target_count = 0
-    for state, count in results.items():
-        if isinstance(state, str):
-            if state == target_str:
-                target_count = count
-                break
-        else:
-            if state == target_int:
-                target_count = count
-                break
-
-    return target_count / total_shots
-
-
 def get_superposition_probabilities(results, num_qubits=1):
     """
     Calculate probabilities for |0⟩ and |1⟩ states in a superposition.
@@ -135,7 +92,9 @@ class TestPauliXGate:
         results = qumat.execute_circuit()
 
         # Calculate probability of expected state
-        prob = get_state_probability(results, expected_state, num_qubits=1)
+        prob = get_state_probability(
+            results, expected_state, num_qubits=1, backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Backend: {backend_name}, "
@@ -170,7 +129,9 @@ class TestPauliXGate:
         results = qumat.execute_circuit()
 
         # Calculate probability of expected state
-        prob = get_state_probability(results, expected_state, 
num_qubits=num_qubits)
+        prob = get_state_probability(
+            results, expected_state, num_qubits=num_qubits, 
backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Backend: {backend_name}, "
@@ -213,7 +174,9 @@ class TestPauliYGate:
         results = qumat.execute_circuit()
 
         # Calculate probability of expected state
-        prob = get_state_probability(results, expected_state, num_qubits=1)
+        prob = get_state_probability(
+            results, expected_state, num_qubits=1, backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Backend: {backend_name}, "
@@ -270,7 +233,9 @@ class TestHadamardGate:
             )
         else:
             # Double application returns to original state
-            prob = get_state_probability(results, initial_state, num_qubits=1)
+            prob = get_state_probability(
+                results, initial_state, num_qubits=1, backend_name=backend_name
+            )
             assert prob > 0.95, (
                 f"Backend: {backend_name}, "
                 f"Initial state: |{initial_state}⟩, "
@@ -314,7 +279,9 @@ class TestNOTGate:
         results = qumat.execute_circuit()
 
         # Calculate probability of expected state
-        prob = get_state_probability(results, expected_state, num_qubits=1)
+        prob = get_state_probability(
+            results, expected_state, num_qubits=1, backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Backend: {backend_name}, "
@@ -394,14 +361,18 @@ class TestUGate:
 
         if expected_behavior == "identity":
             # Should measure |0⟩ with high probability
-            prob = get_state_probability(results, "0", num_qubits=1)
+            prob = get_state_probability(
+                results, "0", num_qubits=1, backend_name=backend_name
+            )
             assert prob > 0.95, (
                 f"Backend: {backend_name}, "
                 f"Expected |0⟩ state after U({theta},{phi},{lambd}), got 
probability {prob:.4f}"
             )
         elif expected_behavior == "pauli_x":
             # Should measure |1⟩ with high probability
-            prob = get_state_probability(results, "1", num_qubits=1)
+            prob = get_state_probability(
+                results, "1", num_qubits=1, backend_name=backend_name
+            )
             assert prob > 0.95, (
                 f"Backend: {backend_name}, "
                 f"Expected |1⟩ state after U({theta},{phi},{lambd}), got 
probability {prob:.4f}"
@@ -517,7 +488,9 @@ class TestPauliZGate:
         results = qumat.execute_circuit()
 
         # Calculate probability of expected state
-        prob = get_state_probability(results, expected_state, num_qubits=1)
+        prob = get_state_probability(
+            results, expected_state, num_qubits=1, backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Backend: {backend_name}, "
@@ -544,7 +517,9 @@ class TestPauliZGate:
         results = qumat.execute_circuit()
 
         # Calculate probability of |1⟩ state
-        prob = get_state_probability(results, "1", num_qubits=1)
+        prob = get_state_probability(
+            results, "1", num_qubits=1, backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Backend: {backend_name}, "
@@ -606,7 +581,9 @@ class TestSingleQubitGatesEdgeCases:
         results = qumat.execute_circuit()
 
         # Calculate probability of |0⟩ state
-        prob = get_state_probability(results, "0", num_qubits=1)
+        prob = get_state_probability(
+            results, "0", num_qubits=1, backend_name=backend_name
+        )
 
         assert prob > 0.95, (
             f"Expected |0⟩ state after gate sequence, got probability {prob}"
@@ -692,7 +669,10 @@ def test_gate_consistency(gate_name, 
expected_state_or_behavior):
         else:
             # For other gates, check specific state probability
             prob = get_state_probability(
-                results, expected_state_or_behavior, num_qubits=1
+                results,
+                expected_state_or_behavior,
+                num_qubits=1,
+                backend_name=backend_name,
             )
             results_dict[backend_name] = prob
 
diff --git a/testing/utils/__init__.py b/testing/utils/__init__.py
index 6c1051bb1..2169a9bd2 100644
--- a/testing/utils/__init__.py
+++ b/testing/utils/__init__.py
@@ -16,6 +16,6 @@
 #
 
 from .constants import TESTING_BACKENDS
-from .qumat_helpers import get_backend_config
+from .qumat_helpers import get_backend_config, get_state_probability
 
-__all__ = ["TESTING_BACKENDS", "get_backend_config"]
+__all__ = ["TESTING_BACKENDS", "get_backend_config", "get_state_probability"]
diff --git a/testing/utils/qumat_helpers.py b/testing/utils/qumat_helpers.py
index ccd1891e2..26337075a 100644
--- a/testing/utils/qumat_helpers.py
+++ b/testing/utils/qumat_helpers.py
@@ -96,3 +96,70 @@ def get_qumat_example_final_state_vector(
     state_vector = qumat_instance.get_final_state_vector()
 
     return state_vector
+
+
+def get_state_probability(results, target_state, num_qubits=1, 
backend_name=None):
+    """
+    Calculate the probability of measuring a target state.
+
+    Args:
+        results: Dictionary of measurement results from execute_circuit()
+        target_state: Target state as string (e.g., "0", "1", "101") or int
+        num_qubits: Number of qubits in the circuit
+        backend_name: Name of the backend (for handling qubit ordering)
+
+    Returns:
+        Probability of measuring the target state
+    """
+    if isinstance(results, list):
+        results = results[0]
+
+    total_shots = sum(results.values())
+    if total_shots == 0:
+        return 0.0
+
+    # Convert target_state to both string and int formats for comparison
+    if isinstance(target_state, str):
+        target_str = target_state
+        # Convert binary string to integer
+        target_int = int(target_state, 2) if target_state else 0
+    else:
+        target_int = target_state
+        # Convert integer to binary string
+        target_str = format(target_state, f"0{num_qubits}b")
+
+    # Handle backend-specific qubit ordering
+    # Qiskit uses little-endian (rightmost bit is qubit 0)
+    # Amazon Braket and Cirq use big-endian (leftmost bit is qubit 0)
+    if backend_name == "qiskit" and isinstance(target_str, str) and 
len(target_str) > 1:
+        # Reverse the string for Qiskit (little-endian)
+        target_str_qiskit = target_str[::-1]
+    else:
+        target_str_qiskit = target_str
+
+    target_count = 0
+    for state, count in results.items():
+        if isinstance(state, str):
+            # For Qiskit, compare with reversed string
+            if backend_name == "qiskit" and len(state) > 1:
+                if state == target_str_qiskit:
+                    target_count = count
+                    break
+            else:
+                if state == target_str:
+                    target_count = count
+                    break
+        else:
+            # For Cirq, use integer comparison
+            # Cirq uses big-endian, so the integer representation matches
+            if backend_name == "cirq":
+                if state == target_int:
+                    target_count = count
+                    break
+            else:
+                # For other backends, also try integer comparison
+                if state == target_int:
+                    target_count = count
+                    break
+
+    return target_count / total_shots

Reply via email to