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 d4a1d7e93 MAHOUT-607: add docstring for qumat.py (#609)
d4a1d7e93 is described below

commit d4a1d7e93adeabb149dd0b2286e64f06a14653cb
Author: GUAN-HAO HUANG <[email protected]>
AuthorDate: Thu Nov 13 10:18:04 2025 +0800

    MAHOUT-607: add docstring for qumat.py (#609)
---
 qumat/qumat.py | 329 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 284 insertions(+), 45 deletions(-)

diff --git a/qumat/qumat.py b/qumat/qumat.py
index 32c7c3fd6..3bccc8c93 100644
--- a/qumat/qumat.py
+++ b/qumat/qumat.py
@@ -18,7 +18,30 @@ from importlib import import_module
 
 
 class QuMat:
+    """Unified interface for quantum circuit operations across multiple 
backends.
+
+    Provides a consistent API for creating and manipulating quantum circuits
+    using different quantum computing backends (Qiskit, Cirq, Amazon Braket).
+    Abstracts backend-specific details for gate operations, circuit execution,
+    and state measurement.
+
+    :param backend_config: Configuration dictionary for the quantum backend.
+        Must contain ``backend_name`` (str) and ``backend_options`` (dict).
+        The ``backend_options`` should include ``simulator_type`` and 
``shots``.
+    :type backend_config: dict
+    """
+
     def __init__(self, backend_config):
+        """Create a QuMat instance with the specified backend configuration.
+
+        :param backend_config: Configuration dictionary containing backend name
+            and options. Required keys:
+            - ``backend_name``: Name of the backend (e.g., "qiskit", "cirq", 
"amazon_braket")
+            - ``backend_options``: Dictionary with backend-specific options
+        :type backend_config: dict
+        :raises ImportError: If the specified backend module cannot be 
imported.
+        :raises KeyError: If required configuration keys are missing.
+        """
         self.backend_config = backend_config
         self.backend_name = backend_config["backend_name"]
         self.backend_module = import_module(
@@ -30,11 +53,25 @@ class QuMat:
         self.parameters = {}
 
     def create_empty_circuit(self, num_qubits: int | None = None):
+        """Create an empty quantum circuit with the specified number of qubits.
+
+        Must be called before applying any gates or executing operations.
+
+        :param num_qubits: Number of qubits in the circuit. If ``None``,
+            creates a circuit without pre-allocated qubits.
+        :type num_qubits: int | None, optional
+        """
         self.num_qubits = num_qubits
         self.circuit = self.backend_module.create_empty_circuit(num_qubits)
 
     def _ensure_circuit_initialized(self):
-        """validate that circuit has been created before operations."""
+        """Ensure the circuit has been created before operations.
+
+        Checks if the circuit has been initialized via 
``create_empty_circuit()``.
+        Raises ``RuntimeError`` if not initialized.
+
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         if self.circuit is None:
             raise RuntimeError(
                 "circuit not initialized. call 
create_empty_circuit(num_qubits) "
@@ -42,14 +79,43 @@ class QuMat:
             )
 
     def apply_not_gate(self, qubit_index):
+        """Apply a NOT gate (Pauli-X gate) to the specified qubit.
+
+        Flips the qubit state from |0⟩ to |1⟩ or |1⟩ to |0⟩.
+        Equivalent to the Pauli-X gate.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_not_gate(self.circuit, qubit_index)
 
     def apply_hadamard_gate(self, qubit_index):
+        """Apply a Hadamard gate to the specified qubit.
+
+        Creates a superposition state, transforming |0⟩ to (|0⟩ + |1⟩)/√2
+        and |1⟩ to (|0⟩ - |1⟩)/√2.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_hadamard_gate(self.circuit, qubit_index)
 
     def apply_cnot_gate(self, control_qubit_index, target_qubit_index):
+        """Apply a Controlled-NOT (CNOT) gate between two qubits.
+
+        Fundamental for entangling qubits. Flips the target qubit if and only
+        if the control qubit is in the |1⟩ state.
+
+        :param control_qubit_index: Index of the control qubit.
+        :type control_qubit_index: int
+        :param target_qubit_index: Index of the target qubit.
+        :type target_qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_cnot_gate(
             self.circuit, control_qubit_index, target_qubit_index
@@ -58,36 +124,111 @@ class QuMat:
     def apply_toffoli_gate(
         self, control_qubit_index1, control_qubit_index2, target_qubit_index
     ):
+        """Apply a Toffoli gate (CCX gate) to three qubits.
+
+        Acts as a quantum AND gate. Flips the target qubit if and only if
+        both control qubits are in the |1⟩ state.
+
+        :param control_qubit_index1: Index of the first control qubit.
+        :type control_qubit_index1: int
+        :param control_qubit_index2: Index of the second control qubit.
+        :type control_qubit_index2: int
+        :param target_qubit_index: Index of the target qubit.
+        :type target_qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_toffoli_gate(
             self.circuit, control_qubit_index1, control_qubit_index2, 
target_qubit_index
         )
 
     def apply_swap_gate(self, qubit_index1, qubit_index2):
+        """Swap the states of two qubits.
+
+        :param qubit_index1: Index of the first qubit.
+        :type qubit_index1: int
+        :param qubit_index2: Index of the second qubit.
+        :type qubit_index2: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_swap_gate(self.circuit, qubit_index1, 
qubit_index2)
 
     def apply_cswap_gate(
         self, control_qubit_index, target_qubit_index1, target_qubit_index2
     ):
+        """Apply a controlled-SWAP (Fredkin) gate.
+
+        Swaps the states of two target qubits if and only if the control
+        qubit is in the |1⟩ state.
+
+        :param control_qubit_index: Index of the control qubit.
+        :type control_qubit_index: int
+        :param target_qubit_index1: Index of the first target qubit.
+        :type target_qubit_index1: int
+        :param target_qubit_index2: Index of the second target qubit.
+        :type target_qubit_index2: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_cswap_gate(
             self.circuit, control_qubit_index, target_qubit_index1, 
target_qubit_index2
         )
 
     def apply_pauli_x_gate(self, qubit_index):
+        """Apply a Pauli-X gate to the specified qubit.
+
+        Equivalent to the NOT gate. Flips the qubit state from |0⟩ to |1⟩
+        or |1⟩ to |0⟩.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_pauli_x_gate(self.circuit, qubit_index)
 
     def apply_pauli_y_gate(self, qubit_index):
+        """Apply a Pauli-Y gate to the specified qubit.
+
+        Rotates the qubit around the Y-axis of the Bloch sphere, affecting
+        both phase and amplitude.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_pauli_y_gate(self.circuit, qubit_index)
 
     def apply_pauli_z_gate(self, qubit_index):
+        """Apply a Pauli-Z gate to the specified qubit.
+
+        Rotates the qubit around the Z-axis of the Bloch sphere, altering
+        the phase without changing the amplitude.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_pauli_z_gate(self.circuit, qubit_index)
 
     def execute_circuit(self, parameter_values=None):
+        """Execute the quantum circuit and return the measurement results.
+
+        Runs the circuit on the configured backend. For parameterized circuits,
+        provide parameter values to bind before execution.
+
+        :param parameter_values: Dictionary mapping parameter names to 
numerical
+            values. Binds these values to circuit parameters before execution.
+        :type parameter_values: dict, optional
+        :returns: Measurement results. Format depends on the backend:
+            - Qiskit/Braket: Dictionary with state strings as keys and counts 
as values
+            - Cirq: List of dictionaries with integer states as keys
+        :rtype: dict | list[dict]
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         if self.num_qubits == 0:
             shots = self.backend_config["backend_options"].get("shots", 1)
@@ -104,6 +245,17 @@ class QuMat:
         )
 
     def bind_parameters(self, parameter_values):
+        """Bind numerical values to circuit parameters.
+
+        Assigns numerical values to symbolic parameters defined in 
parameterized
+        gates.
+
+        :param parameter_values: Dictionary mapping parameter names to 
numerical
+            values.
+        :type parameter_values: dict
+        :raises ValueError: If a parameter name is not found in the circuit's
+            parameter list.
+        """
         for param, value in parameter_values.items():
             if param not in self.parameters:
                 raise ValueError(
@@ -112,52 +264,134 @@ class QuMat:
                 )
             self.parameters[param] = value
 
-    # placeholder method for use in the testing suite
     def get_final_state_vector(self):
+        """Return the final state vector of the quantum circuit.
+
+        The complete quantum state vector after circuit execution,
+        representing the full quantum state of all qubits.
+
+        :returns: The final state vector as a numpy array.
+        :rtype: numpy.ndarray
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         return self.backend_module.get_final_state_vector(
             self.circuit, self.backend, self.backend_config
         )
 
     def draw(self):
+        """Visualize the quantum circuit.
+
+        Generates a visual representation of the circuit. The output format
+        depends on the backend implementation.
+
+        :returns: Circuit visualization. The exact type depends on the backend.
+        :rtype: str | object
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         return self.backend_module.draw_circuit(self.circuit)
 
     def apply_rx_gate(self, qubit_index, angle):
+        """Apply a rotation around the X-axis to the specified qubit.
+
+        Rotates the qubit by the given angle around the X-axis of the Bloch
+        sphere. The angle can be a static value or a parameter name for
+        parameterized circuits.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :param angle: Rotation angle in radians. Can be a float or a string
+            parameter name.
+        :type angle: float | str
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self._handle_parameter(angle)
         self.backend_module.apply_rx_gate(self.circuit, qubit_index, angle)
 
     def apply_ry_gate(self, qubit_index, angle):
+        """Apply a rotation around the Y-axis to the specified qubit.
+
+        Rotates the qubit by the given angle around the Y-axis of the Bloch
+        sphere. The angle can be a static value or a parameter name for
+        parameterized circuits.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :param angle: Rotation angle in radians. Can be a float or a string
+            parameter name.
+        :type angle: float | str
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self._handle_parameter(angle)
         self.backend_module.apply_ry_gate(self.circuit, qubit_index, angle)
 
     def apply_rz_gate(self, qubit_index, angle):
+        """Apply a rotation around the Z-axis to the specified qubit.
+
+        Rotates the qubit by the given angle around the Z-axis of the Bloch
+        sphere. The angle can be a static value or a parameter name for
+        parameterized circuits.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :param angle: Rotation angle in radians. Can be a float or a string
+            parameter name.
+        :type angle: float | str
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self._handle_parameter(angle)
         self.backend_module.apply_rz_gate(self.circuit, qubit_index, angle)
 
     def _handle_parameter(self, param_name):
+        """Register parameter names when parameterized gates are applied.
+
+        Automatically adds string parameter names to the parameters dictionary
+        if not already registered.
+
+        :param param_name: Parameter name to handle. If it's a string,
+            registers it as a parameter.
+        :type param_name: str | float
+        """
         if isinstance(param_name, str) and param_name not in self.parameters:
             self.parameters[param_name] = None
 
     def apply_u_gate(self, qubit_index, theta, phi, lambd):
+        """Apply a U gate (universal single-qubit gate) to the specified qubit.
+
+        A universal single-qubit gate parameterized by three angles (theta,
+        phi, lambd) that can represent any single-qubit unitary operation.
+
+        :param qubit_index: Index of the qubit.
+        :type qubit_index: int
+        :param theta: First rotation angle in radians.
+        :type theta: float
+        :param phi: Second rotation angle in radians.
+        :type phi: float
+        :param lambd: Third rotation angle in radians.
+        :type lambd: float
+        :raises RuntimeError: If the circuit has not been initialized.
+        """
         self._ensure_circuit_initialized()
         self.backend_module.apply_u_gate(self.circuit, qubit_index, theta, 
phi, lambd)
 
     def swap_test(self, ancilla_qubit, qubit1, qubit2):
-        """
-        Implements the swap test circuit for measuring overlap between two 
quantum states.
-
-        The swap test measures the inner product between the states on qubit1 
and qubit2.
-        The probability of measuring the ancilla qubit in state |0> is related 
to the overlap
-        as: P(0) = (1 + |<ψ|φ>|²) / 2
-
-        Args:
-            ancilla_qubit: Index of the ancilla qubit (should be initialized 
to |0>)
-            qubit1: Index of the first qubit containing state |ψ>
-            qubit2: Index of the second qubit containing state |φ>
+        """Implement the swap test circuit for measuring overlap between two 
quantum states.
+
+        Measures the inner product between the states on ``qubit1`` and 
``qubit2``.
+        The probability of measuring the ancilla qubit in state |0⟩ is related
+        to the overlap as: P(0) = (1 + |⟨ψ|φ⟩|²) / 2
+
+        :param ancilla_qubit: Index of the ancilla qubit (should be 
initialized to |0⟩).
+        :type ancilla_qubit: int
+        :param qubit1: Index of the first qubit containing state |ψ⟩.
+        :type qubit1: int
+        :param qubit2: Index of the second qubit containing state |φ⟩.
+        :type qubit2: int
+        :raises RuntimeError: If the circuit has not been initialized.
         """
         # Apply Hadamard to ancilla qubit
         self.apply_hadamard_gate(ancilla_qubit)
@@ -169,27 +403,31 @@ class QuMat:
         self.apply_hadamard_gate(ancilla_qubit)
 
     def measure_overlap(self, qubit1, qubit2, ancilla_qubit=0):
-        """
-        Measures the overlap (fidelity) between two quantum states using the 
swap test.
-
-        This method creates a swap test circuit to calculate the similarity 
between
-        the quantum states on qubit1 and qubit2. It returns the squared 
overlap |<ψ|φ>|²,
-        which represents the fidelity between the two states.
-
-        The swap test measures P(ancilla=0), which is related to overlap as:
-        P(0) = (1 + |<ψ|φ>|²) / 2
-
-        However, for certain states (especially identical excited states), 
global phase
-        effects may cause the ancilla to measure predominantly |1> instead of 
|0>.
-        This method handles both cases by taking the measurement probability 
closer to 1.
-
-        Args:
-            qubit1: Index of the first qubit containing state |ψ>
-            qubit2: Index of the second qubit containing state |φ>
-            ancilla_qubit: Index of the ancilla qubit (default: 0, should be 
initialized to |0>)
-
-        Returns:
-            float: The squared overlap |<ψ|φ>|² between the two states 
(fidelity)
+        """Measure the overlap (fidelity) between two quantum states using the 
swap test.
+
+        Creates a swap test circuit to calculate the similarity between the
+        quantum states on ``qubit1`` and ``qubit2``. Returns the squared 
overlap
+        |⟨ψ|φ⟩|², which represents the fidelity between the two states.
+
+        The swap test measures P(ancilla=0), related to overlap as:
+        P(0) = (1 + |⟨ψ|φ⟩|²) / 2
+
+        For certain states (especially identical excited states), global phase
+        effects may cause the ancilla to measure predominantly |1⟩ instead of 
|0⟩.
+        This method handles both cases by taking the measurement probability
+        closer to 1.
+
+        :param qubit1: Index of the first qubit containing state |ψ⟩.
+        :type qubit1: int
+        :param qubit2: Index of the second qubit containing state |φ⟩.
+        :type qubit2: int
+        :param ancilla_qubit: Index of the ancilla qubit. Default is 0. Should 
be
+            initialized to |0⟩.
+        :type ancilla_qubit: int, optional
+        :returns: The squared overlap |⟨ψ|φ⟩|² between the two states 
(fidelity),
+            clamped to the range [0.0, 1.0].
+        :rtype: float
+        :raises RuntimeError: If the circuit has not been initialized.
         """
         # Perform the swap test
         self.swap_test(ancilla_qubit, qubit1, qubit2)
@@ -204,17 +442,18 @@ class QuMat:
         return overlap_squared
 
     def calculate_prob_zero(self, results, ancilla_qubit):
-        """
-        Calculate the probability of measuring the ancilla qubit in |0> state.
-
-        Delegates to backend-specific implementation via the backend module.
-
-        Args:
-            results: Measurement results from execute_circuit()
-            ancilla_qubit: Index of the ancilla qubit
-
-        Returns:
-            float: Probability of measuring ancilla in |0> state
+        """Calculate the probability of measuring the ancilla qubit in |0⟩ 
state.
+
+        Delegates to the backend-specific implementation. Different backends
+        may use different qubit ordering conventions (little-endian vs 
big-endian).
+
+        :param results: Measurement results from ``execute_circuit()``. Format
+            depends on the backend.
+        :type results: dict | list[dict]
+        :param ancilla_qubit: Index of the ancilla qubit.
+        :type ancilla_qubit: int
+        :returns: Probability of measuring the ancilla qubit in |0⟩ state.
+        :rtype: float
         """
         return self.backend_module.calculate_prob_zero(
             results, ancilla_qubit, self.num_qubits

Reply via email to