From eff5c58445595b84c1b3b71235d34b20c4eeb3b6 Mon Sep 17 00:00:00 2001 From: raunakkumarsingh <35128126+raunakkumarsingh@users.noreply.github.com> Date: Sun, 28 Jul 2024 14:37:27 +0530 Subject: [PATCH 1/3] comment updated --- qdao/circuit.py | 164 +++++++++++++++++++++-------- qdao/converters.py | 41 +++++++- qdao/engine.py | 60 +++++++++-- qdao/exceptions.py | 9 +- qdao/executor.py | 71 ++++++++++++- qdao/manager.py | 63 ++++++++++- qdao/qiskit/annotated_operation.py | 49 +++++++-- qdao/qiskit/circuit.py | 20 +++- qdao/qiskit/gate.py | 113 +++++++++++++++++--- qdao/qiskit/instruction.py | 5 +- qdao/qiskit/simulator.py | 57 ++++++++++ qdao/qiskit/utils.py | 38 +++++-- qdao/qsim/circuit.py | 51 +++++++++ qdao/quafu/circuit.py | 62 +++++++++-- qdao/simulator.py | 71 ++++++++++++- 15 files changed, 768 insertions(+), 106 deletions(-) diff --git a/qdao/circuit.py b/qdao/circuit.py index a4fad40..38483e3 100644 --- a/qdao/circuit.py +++ b/qdao/circuit.py @@ -1,6 +1,19 @@ """ -This module provides methods to partition original circuit -into sub-circuits. +Quantum Circuit Module +=================================== + +This module provides methods to partition original circuit into sub-circuits. + +Classes: + QdaoCircuit: A wrapper for quantum circuits with associated real qubits. + BasePartitioner: Abstract base class for circuit partitioning. + BaselinePartitioner: Implements a naive circuit partitioning approach. + StaticPartitioner: Partitioner that traverses operations in their original order. + DependencyMatrix: Assists the UniQ partitioning algorithm. + UniQPartitioner: Partitioner using the UniQ algorithm. + PartitionerProvider: Provides partitioner instances based on configuration. + CircuitHelperProvider: Provides circuit helper instances based on backend. + """ import logging @@ -11,25 +24,42 @@ class QdaoCircuit: + """ + A class to represent a quantum circuit along with its real qubits. + + Attributes: + circ (Any): The underlying quantum circuit object. + real_qubits (List[int]): List of real qubits associated with the circuit. + """ def __init__(self, circ: Any, real_qubits: List[int]) -> None: self._circ = circ self._real_qubits = real_qubits @property def circ(self) -> Any: + """Gets the underlying quantum circuit.""" return self._circ @circ.setter def circ(self, circ: Any): + """Sets the underlying quantum circuit.""" self._circ = circ @property def real_qubits(self) -> List[int]: + """Gets the list of real qubits.""" return self._real_qubits class BasePartitioner: - """Base class of circuit partition""" + """ + Base class for circuit partitioning. + + Attributes: + np (int): Number of partitions. + nl (int): Number of layers. + backend (str): The backend used for the partitioning process. + """ def __init__(self, np=4, nl=2, backend="qiskit") -> None: self._np = np @@ -38,30 +68,52 @@ def __init__(self, np=4, nl=2, backend="qiskit") -> None: @property def np(self): + """Gets the number of partitions.""" return self._np @np.setter def np(self, n): + """Sets the number of partitions.""" self._np = n @property def nl(self): + """Sets the number of layers.""" return self._nl @nl.setter def nl(self, n): + """Sets the number of layers.""" self._nl = n def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Partitions the given circuit into sub-circuits. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ sub_circs = [] return sub_circs class BaselinePartitioner(BasePartitioner): - """This mimic the naive implementation""" + """A naive implementation of circuit partitioning.""" def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Runs the baseline partitioning algorithm on the given circuit. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ # Set cicuit of circuit helper self._circ_helper.circ = circuit @@ -77,9 +129,18 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: class StaticPartitioner(BasePartitioner): - """Static partitioner which traverse the operations in original order""" + """Static Partitioner that traverses operations in the original order.""" def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Runs the static partitioning algorithm on the given circuit. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ # Set cicuit of circuit helper self._circ_helper.circ = circuit @@ -116,18 +177,18 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: class DependencyMatrix: """To assist the Uniq partitioning algorithm - A two-dimensional matrix is ​used to obtain the dependency between quantum - gates using a dynamic programming method, and based on this dependence, - a sub-circuit containing the most quantum gates that meets the requirements - is generated.We define op[i][j] as putting all operators that include qubit - j in the first i quantum gate and their dependencies into the same group. - - Attributes: - gate_num: An integer holding the number of quantum gates contained in the quantum - circuit - qubit_num: An integer holding the number of qubits acted upon by the quantum circuit - result_bit: the set of all operators - result_op: the set of target a-bits + + A two-dimensional matrix is used to analyze the dependencies between quantum + gates using a dynamic programming method. Based on these dependencies, a sub-circuit + containing the maximum number of quantum gates that meet the specified requirements is generated. + We define `op[i][j]` as the process of grouping all operators that involve qubit `j` + are dependent on the first `i` quantum gates. + + Attributes: + gate_num (int): Number of quantum gates in the circuit. + qubit_num (int): Number of qubits in the circuit. + result_bit (list): A 2D list storing qubit dependencies. + result_op (list): A 2D list storing operator dependencies. """ def __init__( @@ -135,31 +196,32 @@ def __init__( qubit_num: int, gate_num: int, ) -> None: - """Dependency matrix class constructor + """Dependency matrix class constructorInitializes the dependency matrix. Instantiate a dependency matrix class based on the given number of qubits and quantum gates - Arg: - a:An integer representing the number of qubits - b:An integer representing the number of quantum gates + Args: + qubit_num (int): Number of qubits in the circuit. + gate_num (int): Number of quantum gates in the circuit. """ self.gate_num = gate_num self.qubit_num = qubit_num def preprocessing_single_quantum_circuits(self, gate_index: int, gate_target: any): - """Process a single quantum gate, adding it to the dependency matrix + """ + Process a single quantum gate, adding it to the dependency matrix. - Fill in the row of dependency matrix corresponding to the quantum gate. - For the qubit that the quantum gate acts on, combine the quantum gate sets - of all target bits in the previous row and add them to the current quantum - gate and put them into the dependency matrix. And record the qubits these q - uantum gates act on. + Fill in the row of the dependency matrix corresponding to the quantum gate. + For the qubit that the quantum gate acts on, combine the quantum gate sets + of all target bits in the previous row, add them to the current quantum gate, + and put them into the dependency matrix. Record the qubits that these quantum + gates act on. - Arg: - gate_index: An integer representing the index of the quantum gate being processed - gate_target: An integer or list representing the qubits that the quantum gate acts on - """ + Args: + gate_index (int): An integer representing the index of the quantum gate being processed. + gate_target (int or list): An integer or list representing the qubits that the quantum gate acts on. + """ if isinstance(gate_target, int): gate_target = [gate_target] for j in range(self.qubit_num): @@ -178,13 +240,12 @@ def preprocessing_single_quantum_circuits(self, gate_index: int, gate_target: an ) def preprocessing_quantum_circuits(self, ops: list): - """Process all quantum gates, adding it to the dependency matrix - + """Processes all quantum gates and adds them to the dependency matrix. Given a set of quantum gates, use the preprocessing_single_quantum_circuits function to put them all into the dependency matrix - Arg: - ops:A list that stores the quantum gate sequence + Args: + ops (list): A list of quantum gate operations. """ self.result_bit = [ [[] for _ in range(self.qubit_num)] for _ in range(self.gate_num + 1) @@ -206,12 +267,12 @@ def select_subcircuit(self, active_qubit_num: int) -> (List[int], int): Returns a list containing the numbers of all quantum gates and the number of qubits these quantum gates act on. - Arg: - active_qubit_num: An integer representing the required number of active qubits + Args: + active_qubit_num (int): The number of active qubits in the sub-circuit. - Return: - list[int]: - int: + Returns: + List[int]: A list of gate indices forming the sub-circuit. + int: The number of qubits in the sub-circuit. """ qubit_num_subcircuit = 0 gate_num_subcircuit = 0 @@ -229,13 +290,22 @@ def select_subcircuit(self, active_qubit_num: int) -> (List[int], int): class UniQPartitioner(BasePartitioner): - """Partitioner in UniQ + """Partitioner using the UniQ algorithm. References: [1] https://ieeexplore.ieee.org/abstract/document/10045784/ """ def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Runs the UniQ partitioning algorithm on the given circuit. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ self._circ_helper.circ = circuit ops = [] ops += self._circ_helper.instructions @@ -278,6 +348,9 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: class PartitionerProvider: + """ + Provides partitioner instances based on configuration. + """ @classmethod def get_partitioner( cls, @@ -290,4 +363,13 @@ def get_partitioner( class CircuitHelperProvider: @classmethod def get_helper(cls, backend_name: str, **props): + """ + Returns a circuit helper instance based on the specified backend. + + Args: + backend (str): The backend to use (e.g., 'qiskit', 'quafu'). + + Returns: + Any: An instance of a circuit helper. + """ return INITIALIZERS[backend_name](**props) diff --git a/qdao/converters.py b/qdao/converters.py index 13856a3..43df760 100644 --- a/qdao/converters.py +++ b/qdao/converters.py @@ -1,21 +1,58 @@ -"""Converters that convert np.array to statevector objects of other frameworks""" +""" +Converters that convert np.array to statevector objects of other frameworks. + +Classes: + BaseConverter: Abstract base class for statevector converters. + QiskitConverter: Converts np.ndarray to Qiskit Statevector. +""" import numpy as np from qiskit.quantum_info.states import Statevector class BaseConverter: + """ + Base class for statevector converters. + """ + def __init__(self) -> None: + """ + Initializes the BaseConverter. + """ pass def convert(self): + """ + Abstract method to convert statevectors. + + This method should be implemented by subclasses. + """ pass class QiskitConverter(BaseConverter): + """ + Converts np.ndarray to Qiskit Statevector. + + Attributes: + sv (np.ndarray): The numpy array representing the statevector. + """ + def __init__(self, sv: np.ndarray) -> None: + """ + Initializes the QiskitConverter with a statevector. + + Args: + sv (np.ndarray): The numpy array representing the statevector. + """ super().__init__() self._sv = sv - def convert(self): + def convert(self) -> Statevector: + """ + Converts the numpy array to a Qiskit Statevector. + + Returns: + Statevector: The Qiskit Statevector object. + """ return Statevector(self._sv) diff --git a/qdao/engine.py b/qdao/engine.py index 2248911..1f6f940 100644 --- a/qdao/engine.py +++ b/qdao/engine.py @@ -1,3 +1,27 @@ +""" +Quantum Circuit Engine Module +================================ + +This module provides an `Engine` class to execute quantum circuits using various +partitioning strategies and statevector management techniques. The module integrates +with different quantum simulators and supports parallel execution of sub-circuits. + +Modules: +-------- +- qdao.circuit: Contains classes and methods related to quantum circuit partitioning and handling. +- qdao.manager: Manages statevector storage and retrieval. +- qdao.simulator: Provides simulator interfaces for different quantum computing backends. +- qdao.util: Utility functions for safe import, file name generation, and timing. + +Classes: +-------- +- Engine: The main class that handles the execution of quantum circuits. + +Attributes: +----------- +- time_it: A decorator to measure the execution time of methods. +- print_statistics: A function to print execution statistics. +""" import logging from time import time from typing import Any, Optional @@ -19,7 +43,21 @@ class Engine: - """Engine to execute a quantum circuit""" + """ + Engine to execute a quantum circuit. + + Attributes: + _part (BasePartitioner): Partitioner for the circuit. + _sim (Simulator): Simulator object for the backend. + _circ_helper (CircuitHelper): Helper for circuit initialization. + _circ (Any): Quantum circuit to be executed. + _nq (int): Number of qubits in the circuit. + _manager (SvManager): Statevector manager. + _np (int): Number of primary qubits. + _nl (int): Number of local qubits. + _num_chunks (int): Number of chunks for statevector simulation. + + """ def __init__( self, @@ -33,6 +71,18 @@ def __init__( sv_location="disk", **backend_args ) -> None: + """ + Args: + partitioner (Optional[BasePartitioner]): Partitioner for the circuit. + manager (Optional[SvManager]): Statevector manager. + circuit (Any): Quantum circuit to be executed. + num_primary (int): Number of primary qubits. + num_local (int): Number of local qubits. + is_parallel (bool): Whether to run simulations in parallel. + backend (str): Backend simulator to use. + sv_location (str): Location to store statevectors. + **backend_args: Additional arguments for the backend simulator. + """ if isinstance(partitioner, BasePartitioner): self._part = partitioner else: @@ -76,7 +126,7 @@ def num_chunks(self): def _preprocess(self, sub_circ: QdaoCircuit, ichunk: int): """Preprocessing before running a sub-simulation Args: - sub_circ (VirtualCircuit): + sub_circ (VirtualCircuit): Sub-circuit to be processed. ichunk (int): For each sub-circuit, we need to simulate chunk by chunk, (num_chunks = 1<<(nq-np)) """ @@ -145,16 +195,14 @@ def _run(self, sub_circ: QdaoCircuit) -> None: @time_it def _initialize(self): """ - Init storage units to "|000...0>" + Initialize storage units to the state "|000...0>". """ self._manager.initialize() def run(self): """Run simulation 1. Partition the circuit into sub-circuits - 2. For each sub-circuit, run for 1<<(nq-np) times of - simulations. Each simulation will init from a - different part of statevector + 2. For each sub-circuit, run simulations `1<<(nq-np)` times. Each simulation will initialize from a different part of the statevector. """ sub_circs = self._part.run(self._circ) logging.info("Number of sub-circuits: {}".format(len(sub_circs))) diff --git a/qdao/exceptions.py b/qdao/exceptions.py index e019a30..6ff6ab7 100644 --- a/qdao/exceptions.py +++ b/qdao/exceptions.py @@ -1,5 +1,12 @@ -""" Exception for errors raised by QVM """ +""" +Exceptions Module +==================== +This module defines exceptions for errors raised by the Quantum Virtual Machine (QVM). + +Classes: + QdaoError: Base class for errors raised by QVM. +""" class QdaoError(Exception): """Base class for errors raised by QVM""" diff --git a/qdao/executor.py b/qdao/executor.py index dc61a07..d49cf52 100644 --- a/qdao/executor.py +++ b/qdao/executor.py @@ -1,3 +1,17 @@ +""" +Parallel Execution Module +========================= + +This module provides various classes for parallel execution of functions using threads and asynchronous I/O. + +Classes: + ParallelExecutor: Executes a function in parallel using threads. + BatchParallelExecutor: Executes a function in parallel using threads, processing in batches. + PoolParallelExecutor: Executes a function in parallel using a thread pool. + ConstantPoolParallelExecutor: Executes a function in parallel using a constant-sized thread pool. + AsyncIoExecutor: Executes a function in parallel using asyncio for asynchronous I/O operations. +""" + import asyncio import concurrent.futures import multiprocessing as mp @@ -8,11 +22,21 @@ class ParallelExecutor: + """ + Executes a function in parallel using threads. + + Attributes: + _func (callable): The function to execute. + _args (list): The list of arguments for the function. + """ def __init__(self, func, args) -> None: self._func = func self._args = args def execute(self): + """ + Execute the function in parallel using threads. + """ threads = [Thread(target=self._func, args=arg) for arg in self._args] for thread in threads: @@ -23,7 +47,17 @@ def execute(self): class BatchParallelExecutor: + """ + Executes a function in parallel using threads, processing in batches. + """ def execute(self, func, args_list): + """ + Execute the function in parallel using threads, processing in batches. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ cpu_cnt = mp.cpu_count() def split_list(lst, size): @@ -42,7 +76,17 @@ def split_list(lst, size): class PoolParallelExecutor: + """ + Executes a function in parallel using a thread pool. + """ def execute(self, func, args_list): + """ + Execute the function in parallel using a thread pool. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ res = [] with concurrent.futures.ThreadPoolExecutor( @@ -62,8 +106,11 @@ def execute(self, func, args_list): class ConstantPoolParallelExecutor: """ - When NQ is much larger than NL, there will be excessive overhead - Try to run thread pool group by group with each group of CPU_COUNT size + Executes a function in parallel using a constant-sized thread pool. + + When the number of qubits (NQ) is much larger than the number of layers (NL), + there will be excessive overhead. This class tries to run a thread pool group + by group with each group of CPU_COUNT size. """ CPU_CNT = mp.cpu_count() @@ -83,6 +130,13 @@ def _execute(self, func, args_list): print(f"exception! {e}") def execute(self, func, args_list): + """ + Execute the function in parallel using a constant-sized thread pool. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ res = [] def split_list(lst, size): @@ -98,9 +152,11 @@ def split_list(lst, size): class AsyncIoExecutor: """ - When NQ is much larger than NL, there will be excessive overhead - Try to run thread pool group by group with each group of CPU_COUNT size + Executes a function in parallel using asyncio for asynchronous I/O operations. + When the number of qubits (NQ) is much larger than the number of layers (NL), + there will be excessive overhead. This class tries to run thread pool group + by group with each group of CPU_COUNT size. """ CPU_CNT = mp.cpu_count() @@ -117,4 +173,11 @@ async def _execute_one_batch(self, func, args_list): results = await asyncio.gather(*tasks) def execute(self, func, args_list): + """ + Execute the function in parallel using asyncio. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ asyncio.run(self._execute_one_batch(func, args_list)) diff --git a/qdao/manager.py b/qdao/manager.py index 9f6ac25..1d5ae8b 100644 --- a/qdao/manager.py +++ b/qdao/manager.py @@ -1,3 +1,36 @@ +""" +Statevector Manager Module +========================== + +This module provides an `SvManager` class to manage the statevector data access +and storage for quantum circuit simulations. The statevectors can be stored in +memory or on disk, and the class supports both serial and parallel execution +for initializing, loading, and storing statevector chunks. + +Modules: +-------- + +- concurrent.futures: Provides a high-level interface for asynchronously executing callables. +- logging: Provides a flexible framework for emitting log messages from Python programs. +- multiprocessing: Supports spawning processes using an API similar to the threading module. +- os: Provides a portable way of using operating system dependent functionality. +- threading: Constructs higher-level threading interfaces. +- typing: Provides runtime support for type hints. +- numpy: Provides support for large, multi-dimensional arrays and matrices. +- qdao.executor: Contains executor classes for parallel execution. +- qdao.util: Provides utility functions for safe import and file name generation. + +Classes: +-------- + +- SvManager: Manages statevector storage and retrieval. + +Attributes: +----------- + +- print_statistics: A function to print execution statistics. +- time_it: A decorator to measure the execution time of methods. +""" import concurrent.futures import logging import multiprocessing as mp @@ -21,7 +54,24 @@ class SvManager: - """Statevector data access manager""" + """ + Statevector data access manager. + + This class handles the initialization, loading, and storing of statevector chunks + for quantum circuit simulations. Statevectors can be stored in memory or on disk, + and the operations can be performed either serially or in parallel. + + Attributes: + _nq (int): Number of qubits in the target circuit. + _np (int): Number of qubits in primary storage (host memory). + _nl (int): Number of qubits in secondary storage (disk). + _chunk_idx (int): Index of the current chunk. + _chunk (np.ndarray): Current chunk of the statevector. + _is_parallel (bool): Indicates if operations should be parallelized. + _executor (BatchParallelExecutor): Executor for parallel operations. + _global_sv (List[np.ndarray]): List of statevector chunks in memory. + _sv_location (str): Location of statevector storage ('memory' or 'disk'). + """ def __init__( self, @@ -32,11 +82,14 @@ def __init__( sv_location="disk", ) -> None: """ + Initializes the SvManager with the specified parameters. + Args: - num_qubits (int): Number of qubits in the target circuit - num_primary (int): Number of qubits that reside in primary storage (i.e., host memory) - num_local (int): Number of qubits that reside in secondary storage (i.e., disk). - Note that this defines the size of minimum storage unit. + num_qubits (int): Number of qubits in the target circuit. + num_primary (int): Number of qubits in primary storage. + num_local (int): Number of qubits in secondary storage. + is_parallel (bool): Indicates if operations should be parallelized. + sv_location (str): Location of statevector storage ('memory' or 'disk'). """ self._nq, self._np, self._nl = num_qubits, num_primary, num_local self._chunk_idx = 0 diff --git a/qdao/qiskit/annotated_operation.py b/qdao/qiskit/annotated_operation.py index 803baa6..f2351bd 100644 --- a/qdao/qiskit/annotated_operation.py +++ b/qdao/qiskit/annotated_operation.py @@ -10,7 +10,30 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Annotated Operations.""" +""" +Annotated Operations +==================== + +This module defines the `AnnotatedOperation` class and various modifier classes +used to create annotated operations in Qiskit. Annotated operations allow the +addition of modifiers to base operations, enabling more abstract representations +and optimizations during transpilation. + +Classes: +-------- + +- Modifier: Base class for all modifiers. +- InverseModifier: Modifier to specify that the operation is inverted. +- ControlModifier: Modifier to specify that the operation is controlled. +- PowerModifier: Modifier to specify that the operation is raised to a power. +- AnnotatedOperation: Class representing an annotated operation. + +Functions: +---------- + +- _canonicalize_modifiers: Returns the canonical representative of the modifier list. + +""" from __future__ import annotations @@ -84,8 +107,8 @@ def __init__(self, base_op: Operation, modifiers: Union[Modifier, List[Modifier] that is immediately followed by its inverse. Args: - base_op: base operation being modified - modifiers: ordered list of modifiers. Supported modifiers include + base_op (Operation): base operation being modified + modifiers (Union[Modifier, List[Modifier]]): ordered list of modifiers. Supported modifiers include ``InverseModifier``, ``ControlModifier`` and ``PowerModifier``. Examples:: @@ -169,14 +192,14 @@ def control( Implemented as an annotated operation, see :class:`.AnnotatedOperation`. Args: - num_ctrl_qubits: number of controls to add to gate (default: ``1``) - label: ignored (used for consistency with other control methods) - ctrl_state: The control state in decimal or as a bitstring + num_ctrl_qubits (int): number of controls to add to gate (default: ``1``) + label (str | None): ignored (used for consistency with other control methods) + ctrl_state (int | str | None): The control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated: ignored (used for consistency with other control methods) + annotated (bool): ignored (used for consistency with other control methods) Returns: - Controlled version of the given operation. + AnnotatedOperation: Controlled version of the given operation. """ # pylint: disable=unused-argument extended_modifiers = self.modifiers.copy() @@ -192,10 +215,10 @@ def inverse(self, annotated: bool = True): Implemented as an annotated operation, see :class:`.AnnotatedOperation`. Args: - annotated: ignored (used for consistency with other inverse methods) + annotated (bool): ignored (used for consistency with other inverse methods) Returns: - Inverse version of the given operation. + AnnotatedOperation: Inverse version of the given operation. """ # pylint: disable=unused-argument extended_modifiers = self.modifiers.copy() @@ -211,6 +234,12 @@ def _canonicalize_modifiers(modifiers): of control qubits / control state and the total power. The InverseModifier will be present if total power is negative, whereas the power modifier will be present only with positive powers different from 1. + + Args: + modifiers (List[Modifier]): List of modifiers to canonicalize. + + Returns: + List[Modifier]: Canonical list of modifiers. """ power = 1 num_ctrl_qubits = 0 diff --git a/qdao/qiskit/circuit.py b/qdao/qiskit/circuit.py index a6253c6..36d0df3 100644 --- a/qdao/qiskit/circuit.py +++ b/qdao/qiskit/circuit.py @@ -11,6 +11,14 @@ class QiskitCircuitWrapper(BaseCircWrapper): + """ + Wrapper class for Qiskit QuantumCircuit. + + Provides additional functionalities and interface for interaction with QDAO. + + Args: + circ (Optional[QuantumCircuit]): Qiskit QuantumCircuit instance. + """ def __init__(self, circ: Optional[QuantumCircuit] = None) -> None: self._circ = circ or None @@ -40,6 +48,9 @@ def get_instr_qubits(self, instruction: CircuitInstruction): def init_circ_from_sv(self, sv: np.ndarray): """Initialize qiskit QuantumCircuit from given statevector + + Args: + sv (np.ndarray): Statevector to initialize the circuit with. Comments: 1. Currently qiskit QuantumCircuit.initialize will @@ -69,9 +80,12 @@ def gen_sub_circ( instructions Args: - instrs (List[CircuitInstruction]): A list of instructions - Return: - QdaoCircuit + instrs (List[CircuitInstruction]): A list of instructions. + num_local (int): Number of local qubits. + num_primary (int): Number of primary qubits. + + Returns: + QdaoCircuit: Generated sub-circuit. """ if not isinstance(self._circ, QuantumCircuit): raise ValueError("Please set self._circ") diff --git a/qdao/qiskit/gate.py b/qdao/qiskit/gate.py index c4a3138..d5304de 100644 --- a/qdao/qiskit/gate.py +++ b/qdao/qiskit/gate.py @@ -10,7 +10,50 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Unitary gate.""" +""" +Unitary Gate Module +==================== + +This module provides a `Gate` class to represent unitary gates in quantum circuits. +It includes methods for matrix conversion, exponentiation, control, and argument broadcasting. + +Modules: +-------- +- qiskit.circuit.exceptions: Contains exceptions related to quantum circuits. +- qiskit.circuit.parameterexpression: Handles parameter expressions in quantum circuits. +- .annotated_operation: Provides classes for annotated operations. +- .instruction: Defines the base `Instruction` class. + +Classes: +-------- +- Gate: Represents a unitary gate in a quantum circuit. + +Methods: +-------- +- __init__(self, name: str, num_qubits: int, params: list, label: str | None = None, duration: Any = None, unit: str = "dt") + Create a new gate. +- to_matrix(self) -> np.ndarray + Return a Numpy.array for the gate unitary matrix. +- power(self, exponent: float) + Creates a unitary gate as `gate^exponent`. +- __pow__(self, exponent: float) -> "Gate" + Allows exponentiation using the `**` operator. +- _return_repeat(self, exponent: float) -> "Gate" + Returns a repeated version of the gate. +- control(self, num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: int | str | None = None, annotated: bool = False) + Return the controlled version of itself. +- _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]] + Expands a single argument for broadcasting. +- _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, list]] + Expands two arguments for broadcasting. +- _broadcast_3_or_more_args(qargs: list) -> Iterator[tuple[list, list]] + Expands three or more arguments for broadcasting. +- broadcast_arguments(self, qargs: list, cargs: list) -> Iterable[tuple[list, list]] + Validation and handling of the arguments and their relationships. +- validate_parameter(self, parameter: Any) + Gate parameters should be int, float, or ParameterExpression. + +""" from __future__ import annotations @@ -39,10 +82,12 @@ def __init__( """Create a new gate. Args: - name: The Qobj name of the gate. - num_qubits: The number of qubits the gate acts on. - params: A list of parameters. - label: An optional label for the gate. + name (str): The Qobj name of the gate. + num_qubits (int): The number of qubits the gate acts on. + params (list): A list of parameters. + label (str | None): An optional label for the gate. + duration (Any): The duration of the gate. + unit (str): The unit of the gate duration. """ self.definition = None super().__init__( @@ -109,13 +154,13 @@ def control( Implemented either as a controlled gate (ref. :class:`.ControlledGate`) or as an annotated operation (ref. :class:`.AnnotatedOperation`). - Args: - num_ctrl_qubits: number of controls to add to gate (default: ``1``) - label: optional gate label. Ignored if implemented as an annotated + Args: + num_ctrl_qubits (int): number of controls to add to gate (default: ``1``) + label (str | None): optional gate label. Ignored if implemented as an annotated operation. - ctrl_state: the control state in decimal or as a bitstring + ctrl_state (int | str | None): the control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated: indicates whether the controlled gate can be implemented + annotated (bool): indicates whether the controlled gate can be implemented as an annotated gate. Returns: @@ -139,7 +184,13 @@ def control( @staticmethod def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: """Expands a single argument. - + + Args: + qarg (list): List of qubit arguments. + + Returns: + Iterator[tuple[list, list]]: Iterator of expanded arguments. + For example: [q[0], q[1]] -> [q[0]], [q[1]] """ # [q[0], q[1]] -> [q[0]] @@ -149,6 +200,18 @@ def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: @staticmethod def _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, list]]: + """Expands two arguments for broadcasting. + + Args: + qarg0 (list): First list of qubit arguments. + qarg1 (list): Second list of qubit arguments. + + Returns: + Iterator[tuple[list, list]]: Iterator of expanded arguments. + + Raises: + CircuitError: If the arguments cannot be combined. + """ if len(qarg0) == len(qarg1): # [[q[0], q[1]], [r[0], r[1]]] -> [q[0], r[0]] # -> [q[1], r[1]] @@ -171,6 +234,18 @@ def _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, lis @staticmethod def _broadcast_3_or_more_args(qargs: list) -> Iterator[tuple[list, list]]: + """Expands three or more arguments for broadcasting. + + Args: + qargs (list): List of lists of qubit arguments. + + Returns: + Iterator[tuple[list, list]]: Iterator of expanded arguments. + + Raises: + CircuitError: If the arguments cannot be combined. + """ + if all(len(qarg) == len(qargs[0]) for qarg in qargs): for arg in zip(*qargs): yield list(arg), [] @@ -208,15 +283,16 @@ def broadcast_arguments( [q[0], q[1]], [r[0], r[1]], ...] -> [q[0], r[0], ...], [q[1], r[1], ...] Args: - qargs: List of quantum bit arguments. - cargs: List of classical bit arguments. + qargs (list): List of quantum bit arguments. + cargs (list): List of classical bit arguments. Returns: - A tuple with single arguments. + Iterable[tuple[list, list]]: A tuple with single arguments. Raises: CircuitError: If the input is not valid. For example, the number of arguments does not match the gate expectation. + """ if len(qargs) != self.num_qubits or cargs: raise CircuitError( @@ -241,7 +317,14 @@ def broadcast_arguments( raise CircuitError("This gate cannot handle %i arguments" % len(qargs)) def validate_parameter(self, parameter): - """Gate parameters should be int, float, or ParameterExpression""" + """Gate parameters should be int, float, or ParameterExpression + + Args: + parameter (Any): Parameter to validate. + + Raises: + CircuitError: If the parameter is invalid. + """ if isinstance(parameter, ParameterExpression): if len(parameter.parameters) > 0: return ( diff --git a/qdao/qiskit/instruction.py b/qdao/qiskit/instruction.py index ecf8cea..560a378 100644 --- a/qdao/qiskit/instruction.py +++ b/qdao/qiskit/instruction.py @@ -11,7 +11,10 @@ # that they have been altered from the originals. """ -A generic quantum instruction. +Qiskit Instruction Module +========================== + +This module contains the definition and functionality of a generic quantum instruction. Instructions can be implementable on hardware (u, cx, etc.) or in simulation (snapshot, noise, etc.). diff --git a/qdao/qiskit/simulator.py b/qdao/qiskit/simulator.py index 9289870..84fe6d2 100644 --- a/qdao/qiskit/simulator.py +++ b/qdao/qiskit/simulator.py @@ -1,3 +1,24 @@ +""" +Qiskit Simulator Module +======================== + +This module provides a `QiskitSimulator` class to interface with Qiskit's Aer simulator and other +potential simulators. It includes methods for initialization and running simulations. + +Modules: +-------- +- logging: Provides logging capabilities. +- typing: Includes the Optional type hint. +- numpy: Handles numerical operations and arrays. +- qiskit_aer: Provides access to Qiskit's Aer simulator. +- qdao.exceptions: Contains custom exceptions for the quantum DAO (Qdao) module. + +Classes: +-------- +- QiskitSimulator: Interfaces with Qiskit simulators for running quantum circuit simulations. + + +""" import logging from typing import Optional @@ -7,12 +28,36 @@ class QiskitSimulator: + """ + A class to interface with Qiskit's Aer simulator and other potential simulators. + + Attributes: + ----------- + _sim : Backend + The backend simulator used for running quantum simulations. + + Methods: + -------- + __init__(self, provider: Optional[str] = None, fusion: Optional[bool] = False, device: str = "CPU") -> None + Initializes the simulator with the specified provider, fusion option, and device type. + + run(self, simobj) -> np.ndarray + Runs a simulation on the provided simulation object and returns the resulting state vector. + """ def __init__( self, provider: Optional[str] = None, fusion: Optional[bool] = False, device: str = "CPU", ) -> None: + """ + Initialize the QiskitSimulator. + + Args: + provider (Optional[str]): The simulator provider to use (e.g., 'ddsim'). Defaults to None. + fusion (Optional[bool]): Whether to enable fusion optimizations. Defaults to False. + device (str): The device to use for simulation ('CPU' or 'GPU'). Defaults to 'CPU'. + """ if provider: if provider == "ddsim": from mqt import ddsim @@ -26,6 +71,18 @@ def __init__( self._sim.set_options(device=device) def run(self, simobj) -> np.ndarray: + """ + Run a simulation on the provided simulation object. + + Args: + simobj: The simulation object containing the quantum circuit to simulate. + + Returns: + np.ndarray: The resulting state vector from the simulation. + + Raises: + QdaoError: If the simulation fails. + """ res = self._sim.run(simobj.circ).result() if not res.success: raise QdaoError( diff --git a/qdao/qiskit/utils.py b/qdao/qiskit/utils.py index 81a7cb6..a976c93 100644 --- a/qdao/qiskit/utils.py +++ b/qdao/qiskit/utils.py @@ -10,7 +10,23 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Utility functions for generating random circuits.""" +""" +Utility Functions for Generating Random Circuits +================================================= + +This module provides utility functions for generating random quantum circuits. + +Modules: +-------- +- numpy: Handles numerical operations and arrays. +- qiskit.circuit: Provides the QuantumCircuit, QuantumRegister, ClassicalRegister, and exceptions for quantum circuits. +- qiskit.circuit.library.standard_gates: Contains standard gates for quantum circuits. + +Functions: +---------- +- random_circuit(num_qubits: int, depth: int, max_operands: int = 3, measure: bool = False, conditional: bool = False, reset: bool = False, seed: int = None) -> QuantumCircuit + Generate a random quantum circuit with the specified parameters. +""" import numpy as np from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister, Reset @@ -66,20 +82,20 @@ def random_circuit( circ = random_circuit(2, 2, measure=True) circ.draw(output='mpl') - Args: - num_qubits (int): number of quantum wires - depth (int): layers of operations (i.e. critical path length) - max_operands (int): maximum operands of each gate (between 1 and 3) - measure (bool): if True, measure all qubits at the end - conditional (bool): if True, insert middle measurements and conditionals - reset (bool): if True, insert middle resets - seed (int): sets random seed (optional) + Args: + num_qubits (int): Number of quantum wires. + depth (int): Layers of operations (i.e. critical path length). + max_operands (int): Maximum operands of each gate (between 1 and 3). Defaults to 3. + measure (bool): If True, measure all qubits at the end. Defaults to False. + conditional (bool): If True, insert middle measurements and conditionals. Defaults to False. + reset (bool): If True, insert middle resets. Defaults to False. + seed (int): Sets random seed (optional). Defaults to None. Returns: - QuantumCircuit: constructed circuit + QuantumCircuit: Constructed random quantum circuit. Raises: - CircuitError: when invalid options given + CircuitError: When invalid options are given. """ if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") diff --git a/qdao/qsim/circuit.py b/qdao/qsim/circuit.py index 4ef3b8c..2b13a4a 100644 --- a/qdao/qsim/circuit.py +++ b/qdao/qsim/circuit.py @@ -1,3 +1,30 @@ +""" +Qsim Circuit Helper Module +========================== + +This module provides the `QsimCircuitHelper` class, which is used to manage and manipulate +quantum circuits specific to the Qsim framework. The helper class facilitates various +operations such as initializing circuits from state vectors, generating sub-circuits, and +retrieving instructions and qubit mappings. + +Modules: +-------- + +- qdao.circuit: Contains the QdaoCircuit class for representing quantum circuits. +- cirq.circuits: Contains the Circuit class for representing quantum circuits in Cirq. +- cirq.ops.gate_operation: Contains the GateOperation class for representing gate operations. + +Classes: +-------- + +- QsimCircuitHelper: Helper class for managing Qsim quantum circuits. + +Attributes: +----------- + +- Circuit: The main class for representing a quantum circuit in Cirq. +- GateOperation: The class for representing gate operations in Cirq. +""" import copy import logging from typing import List, Optional @@ -26,7 +53,31 @@ class QsimCircuitHelper: + """ + Helper class for managing and manipulating Qsim quantum circuits. + + This class provides methods for initializing circuits from state vectors, generating + sub-circuits based on a list of instructions, and retrieving instructions and qubit + mappings from a given circuit. + + Attributes: + _circ (Circuit): The quantum circuit object. + + Methods: + circ: Returns or sets the quantum circuit object. + num_qubits: Returns the number of qubits in the circuit. + instructions: Returns the list of instructions in the circuit. + get_instr_qubits: Returns the qubits involved in a given instruction. + init_circ_from_sv: Initializes a circuit from a given state vector. + gen_sub_circ: Generates a sub-circuit based on a list of instructions. + """ def __init__(self, circ: Optional[Circuit] = None) -> None: + """ + Initializes the QsimCircuitHelper with an optional quantum circuit. + + Args: + circ (Circuit, optional): The quantum circuit object. + """ self._circ = circ or None self._circ.num = 0 self._circ.itrs = [] diff --git a/qdao/quafu/circuit.py b/qdao/quafu/circuit.py index bbb2586..a8ad608 100644 --- a/qdao/quafu/circuit.py +++ b/qdao/quafu/circuit.py @@ -1,3 +1,31 @@ +""" +Quafu Circuit Module +=========================== + +This module provides the `QuafuCircuitHelper` class, which is used to manage and manipulate +quantum circuits specific to the Quafu framework. The helper class facilitates various +operations such as initializing circuits from state vectors, generating sub-circuits, and +retrieving instructions and qubit mappings. + +Modules: +-------- + +- qdao.base_circuit_wrapper: Contains the base class for circuit wrappers. +- quafu.circuits.quantum_circuit: Contains the QuantumCircuit and related classes. + +Classes: +-------- + +- QuafuCircuitHelper: Helper class for managing Quafu quantum circuits. + +Attributes: +----------- + +- QuantumCircuit: The main class for representing a quantum circuit. +- QuantumGate: The base class for quantum gates. +- SingleQubitGate: Represents single-qubit gates. +- ControlledGate: Represents controlled gates. +""" import copy import logging from typing import List, Optional @@ -13,6 +41,24 @@ class QuafuCircuitHelper(BaseCircWrapper): + """ + Helper class for managing and manipulating Quafu quantum circuits. + + This class provides methods for initializing circuits from state vectors, generating + sub-circuits based on a list of instructions, and retrieving instructions and qubit + mappings from a given circuit. + + Attributes: + _circ (QuantumCircuit): The quantum circuit object. + + Methods: + circ: Returns or sets the quantum circuit object. + num_qubits: Returns the number of qubits in the circuit. + instructions: Returns the list of instructions in the circuit. + get_instr_qubits: Returns the qubits involved in a given instruction. + init_circ_from_sv: Initializes a circuit from a given state vector. + gen_sub_circ: Generates a sub-circuit based on a list of instructions. + """ def __init__(self, circ: Optional[QuantumCircuit] = None) -> None: self._circ = circ or None @@ -56,14 +102,18 @@ def init_circ_from_sv(self, sv: np.ndarray): return QdaoSimObj(sv, self._circ) def gen_sub_circ(self, instrs: List[QuantumGate], num_local: int, num_primary: int): - """Generate a sub circuit based on a list of circuit instructions - We assume there's no conditional instructions and no measurement - instructions + """ + Generates a sub-circuit based on a list of circuit instructions. + + Assumes there are no conditional instructions and no measurement instructions. Args: - instrs (List[QuantumGate]): A list of instructions - Return: - QdaoCircuit + instrs (List[QuantumGate]): A list of instructions. + num_local (int): The number of local qubits. + num_primary (int): The number of primary qubits. + + Returns: + QdaoCircuit: The generated sub-circuit. """ if not isinstance(self._circ, QuantumCircuit): raise ValueError("Please set self._circ") diff --git a/qdao/simulator.py b/qdao/simulator.py index b5229fb..851312b 100644 --- a/qdao/simulator.py +++ b/qdao/simulator.py @@ -1,25 +1,71 @@ -"""Simulator provider""" +""" +Simulator Provider Module +========================= + +This module provides the `SimulatorProvider` class and associated objects for managing +and interacting with different quantum simulators. The simulators can be accessed through +a unified interface, making it easy to switch between different backend providers. + +Modules: +-------- + +- qdao.qiskit.simulator: Contains the QiskitSimulator class. +- qdao.quafu.simulator: Contains the QuafuSimulator class. + +Classes: +-------- + +- QdaoSimObj: Represents a simulation object containing the circuit and run options. +- SimulatorProvider: Provides access to different quantum simulators based on backend name. + +Attributes: +----------- + +- SIMS: A dictionary mapping backend names to their respective simulator classes. +""" from qdao.qiskit.simulator import QiskitSimulator from qdao.quafu.simulator import QuafuSimulator class QdaoSimObj: + """ + Simulation object for managing quantum circuits and run options. + + This class encapsulates the quantum circuit and any additional options required + to run the simulation. + + Attributes: + _objs (tuple): Tuple containing the simulation objects. + _circ (Any): The quantum circuit object. + _run_options (dict): Options for running the simulation. + """ + def __init__(self, *objs, **options) -> None: + """ + Initializes the QdaoSimObj with the provided objects and options. + + Args: + objs (tuple): Simulation objects. + options (dict): Run options for the simulation. + """ self._objs = objs self._circ = objs[-1] self._run_options = options @property def circ(self): + """Returns the quantum circuit object.""" return self._circ @property def objs(self): + """Returns the simulation objects.""" return self._objs @property def run_options(self): + """Returns the run options for the simulation.""" return self._run_options @@ -27,6 +73,29 @@ def run_options(self): class SimulatorProvider: + """ + Provides access to different quantum simulators. + + This class allows the selection and initialization of a quantum simulator + based on the specified backend name. + + Methods: + get_simulator: Returns an instance of the specified simulator backend. + + Attributes: + SIMS (dict): Dictionary mapping backend names to simulator classes. + """ + @classmethod def get_simulator(cls, backend_name: str, **kwargs): + """ + Returns an instance of the specified simulator backend. + + Args: + backend_name (str): The name of the backend simulator. + kwargs (dict): Additional arguments for the simulator. + + Returns: + Simulator: An instance of the requested simulator backend. + """ return SIMS[backend_name](**kwargs) From a06fed3670630d1439d6f5b2558cea4ad9c01479 Mon Sep 17 00:00:00 2001 From: raunakkumarsingh <35128126+raunakkumarsingh@users.noreply.github.com> Date: Sun, 28 Jul 2024 14:37:27 +0530 Subject: [PATCH 2/3] comment updated --- qdao/circuit.py | 164 +++++++++++++++++++++-------- qdao/converters.py | 41 +++++++- qdao/engine.py | 60 +++++++++-- qdao/exceptions.py | 9 +- qdao/executor.py | 71 ++++++++++++- qdao/manager.py | 63 ++++++++++- qdao/qiskit/annotated_operation.py | 49 +++++++-- qdao/qiskit/circuit.py | 20 +++- qdao/qiskit/gate.py | 113 +++++++++++++++++--- qdao/qiskit/instruction.py | 5 +- qdao/qiskit/simulator.py | 57 ++++++++++ qdao/qiskit/utils.py | 38 +++++-- qdao/qsim/circuit.py | 51 +++++++++ qdao/quafu/circuit.py | 62 +++++++++-- qdao/simulator.py | 71 ++++++++++++- 15 files changed, 768 insertions(+), 106 deletions(-) diff --git a/qdao/circuit.py b/qdao/circuit.py index a4fad40..38483e3 100644 --- a/qdao/circuit.py +++ b/qdao/circuit.py @@ -1,6 +1,19 @@ """ -This module provides methods to partition original circuit -into sub-circuits. +Quantum Circuit Module +=================================== + +This module provides methods to partition original circuit into sub-circuits. + +Classes: + QdaoCircuit: A wrapper for quantum circuits with associated real qubits. + BasePartitioner: Abstract base class for circuit partitioning. + BaselinePartitioner: Implements a naive circuit partitioning approach. + StaticPartitioner: Partitioner that traverses operations in their original order. + DependencyMatrix: Assists the UniQ partitioning algorithm. + UniQPartitioner: Partitioner using the UniQ algorithm. + PartitionerProvider: Provides partitioner instances based on configuration. + CircuitHelperProvider: Provides circuit helper instances based on backend. + """ import logging @@ -11,25 +24,42 @@ class QdaoCircuit: + """ + A class to represent a quantum circuit along with its real qubits. + + Attributes: + circ (Any): The underlying quantum circuit object. + real_qubits (List[int]): List of real qubits associated with the circuit. + """ def __init__(self, circ: Any, real_qubits: List[int]) -> None: self._circ = circ self._real_qubits = real_qubits @property def circ(self) -> Any: + """Gets the underlying quantum circuit.""" return self._circ @circ.setter def circ(self, circ: Any): + """Sets the underlying quantum circuit.""" self._circ = circ @property def real_qubits(self) -> List[int]: + """Gets the list of real qubits.""" return self._real_qubits class BasePartitioner: - """Base class of circuit partition""" + """ + Base class for circuit partitioning. + + Attributes: + np (int): Number of partitions. + nl (int): Number of layers. + backend (str): The backend used for the partitioning process. + """ def __init__(self, np=4, nl=2, backend="qiskit") -> None: self._np = np @@ -38,30 +68,52 @@ def __init__(self, np=4, nl=2, backend="qiskit") -> None: @property def np(self): + """Gets the number of partitions.""" return self._np @np.setter def np(self, n): + """Sets the number of partitions.""" self._np = n @property def nl(self): + """Sets the number of layers.""" return self._nl @nl.setter def nl(self, n): + """Sets the number of layers.""" self._nl = n def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Partitions the given circuit into sub-circuits. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ sub_circs = [] return sub_circs class BaselinePartitioner(BasePartitioner): - """This mimic the naive implementation""" + """A naive implementation of circuit partitioning.""" def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Runs the baseline partitioning algorithm on the given circuit. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ # Set cicuit of circuit helper self._circ_helper.circ = circuit @@ -77,9 +129,18 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: class StaticPartitioner(BasePartitioner): - """Static partitioner which traverse the operations in original order""" + """Static Partitioner that traverses operations in the original order.""" def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Runs the static partitioning algorithm on the given circuit. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ # Set cicuit of circuit helper self._circ_helper.circ = circuit @@ -116,18 +177,18 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: class DependencyMatrix: """To assist the Uniq partitioning algorithm - A two-dimensional matrix is ​used to obtain the dependency between quantum - gates using a dynamic programming method, and based on this dependence, - a sub-circuit containing the most quantum gates that meets the requirements - is generated.We define op[i][j] as putting all operators that include qubit - j in the first i quantum gate and their dependencies into the same group. - - Attributes: - gate_num: An integer holding the number of quantum gates contained in the quantum - circuit - qubit_num: An integer holding the number of qubits acted upon by the quantum circuit - result_bit: the set of all operators - result_op: the set of target a-bits + + A two-dimensional matrix is used to analyze the dependencies between quantum + gates using a dynamic programming method. Based on these dependencies, a sub-circuit + containing the maximum number of quantum gates that meet the specified requirements is generated. + We define `op[i][j]` as the process of grouping all operators that involve qubit `j` + are dependent on the first `i` quantum gates. + + Attributes: + gate_num (int): Number of quantum gates in the circuit. + qubit_num (int): Number of qubits in the circuit. + result_bit (list): A 2D list storing qubit dependencies. + result_op (list): A 2D list storing operator dependencies. """ def __init__( @@ -135,31 +196,32 @@ def __init__( qubit_num: int, gate_num: int, ) -> None: - """Dependency matrix class constructor + """Dependency matrix class constructorInitializes the dependency matrix. Instantiate a dependency matrix class based on the given number of qubits and quantum gates - Arg: - a:An integer representing the number of qubits - b:An integer representing the number of quantum gates + Args: + qubit_num (int): Number of qubits in the circuit. + gate_num (int): Number of quantum gates in the circuit. """ self.gate_num = gate_num self.qubit_num = qubit_num def preprocessing_single_quantum_circuits(self, gate_index: int, gate_target: any): - """Process a single quantum gate, adding it to the dependency matrix + """ + Process a single quantum gate, adding it to the dependency matrix. - Fill in the row of dependency matrix corresponding to the quantum gate. - For the qubit that the quantum gate acts on, combine the quantum gate sets - of all target bits in the previous row and add them to the current quantum - gate and put them into the dependency matrix. And record the qubits these q - uantum gates act on. + Fill in the row of the dependency matrix corresponding to the quantum gate. + For the qubit that the quantum gate acts on, combine the quantum gate sets + of all target bits in the previous row, add them to the current quantum gate, + and put them into the dependency matrix. Record the qubits that these quantum + gates act on. - Arg: - gate_index: An integer representing the index of the quantum gate being processed - gate_target: An integer or list representing the qubits that the quantum gate acts on - """ + Args: + gate_index (int): An integer representing the index of the quantum gate being processed. + gate_target (int or list): An integer or list representing the qubits that the quantum gate acts on. + """ if isinstance(gate_target, int): gate_target = [gate_target] for j in range(self.qubit_num): @@ -178,13 +240,12 @@ def preprocessing_single_quantum_circuits(self, gate_index: int, gate_target: an ) def preprocessing_quantum_circuits(self, ops: list): - """Process all quantum gates, adding it to the dependency matrix - + """Processes all quantum gates and adds them to the dependency matrix. Given a set of quantum gates, use the preprocessing_single_quantum_circuits function to put them all into the dependency matrix - Arg: - ops:A list that stores the quantum gate sequence + Args: + ops (list): A list of quantum gate operations. """ self.result_bit = [ [[] for _ in range(self.qubit_num)] for _ in range(self.gate_num + 1) @@ -206,12 +267,12 @@ def select_subcircuit(self, active_qubit_num: int) -> (List[int], int): Returns a list containing the numbers of all quantum gates and the number of qubits these quantum gates act on. - Arg: - active_qubit_num: An integer representing the required number of active qubits + Args: + active_qubit_num (int): The number of active qubits in the sub-circuit. - Return: - list[int]: - int: + Returns: + List[int]: A list of gate indices forming the sub-circuit. + int: The number of qubits in the sub-circuit. """ qubit_num_subcircuit = 0 gate_num_subcircuit = 0 @@ -229,13 +290,22 @@ def select_subcircuit(self, active_qubit_num: int) -> (List[int], int): class UniQPartitioner(BasePartitioner): - """Partitioner in UniQ + """Partitioner using the UniQ algorithm. References: [1] https://ieeexplore.ieee.org/abstract/document/10045784/ """ def run(self, circuit: Any) -> List[QdaoCircuit]: + """ + Runs the UniQ partitioning algorithm on the given circuit. + + Args: + circuit (Any): The quantum circuit to be partitioned. + + Returns: + List[QdaoCircuit]: A list of partitioned sub-circuits. + """ self._circ_helper.circ = circuit ops = [] ops += self._circ_helper.instructions @@ -278,6 +348,9 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: class PartitionerProvider: + """ + Provides partitioner instances based on configuration. + """ @classmethod def get_partitioner( cls, @@ -290,4 +363,13 @@ def get_partitioner( class CircuitHelperProvider: @classmethod def get_helper(cls, backend_name: str, **props): + """ + Returns a circuit helper instance based on the specified backend. + + Args: + backend (str): The backend to use (e.g., 'qiskit', 'quafu'). + + Returns: + Any: An instance of a circuit helper. + """ return INITIALIZERS[backend_name](**props) diff --git a/qdao/converters.py b/qdao/converters.py index 13856a3..43df760 100644 --- a/qdao/converters.py +++ b/qdao/converters.py @@ -1,21 +1,58 @@ -"""Converters that convert np.array to statevector objects of other frameworks""" +""" +Converters that convert np.array to statevector objects of other frameworks. + +Classes: + BaseConverter: Abstract base class for statevector converters. + QiskitConverter: Converts np.ndarray to Qiskit Statevector. +""" import numpy as np from qiskit.quantum_info.states import Statevector class BaseConverter: + """ + Base class for statevector converters. + """ + def __init__(self) -> None: + """ + Initializes the BaseConverter. + """ pass def convert(self): + """ + Abstract method to convert statevectors. + + This method should be implemented by subclasses. + """ pass class QiskitConverter(BaseConverter): + """ + Converts np.ndarray to Qiskit Statevector. + + Attributes: + sv (np.ndarray): The numpy array representing the statevector. + """ + def __init__(self, sv: np.ndarray) -> None: + """ + Initializes the QiskitConverter with a statevector. + + Args: + sv (np.ndarray): The numpy array representing the statevector. + """ super().__init__() self._sv = sv - def convert(self): + def convert(self) -> Statevector: + """ + Converts the numpy array to a Qiskit Statevector. + + Returns: + Statevector: The Qiskit Statevector object. + """ return Statevector(self._sv) diff --git a/qdao/engine.py b/qdao/engine.py index 2248911..1f6f940 100644 --- a/qdao/engine.py +++ b/qdao/engine.py @@ -1,3 +1,27 @@ +""" +Quantum Circuit Engine Module +================================ + +This module provides an `Engine` class to execute quantum circuits using various +partitioning strategies and statevector management techniques. The module integrates +with different quantum simulators and supports parallel execution of sub-circuits. + +Modules: +-------- +- qdao.circuit: Contains classes and methods related to quantum circuit partitioning and handling. +- qdao.manager: Manages statevector storage and retrieval. +- qdao.simulator: Provides simulator interfaces for different quantum computing backends. +- qdao.util: Utility functions for safe import, file name generation, and timing. + +Classes: +-------- +- Engine: The main class that handles the execution of quantum circuits. + +Attributes: +----------- +- time_it: A decorator to measure the execution time of methods. +- print_statistics: A function to print execution statistics. +""" import logging from time import time from typing import Any, Optional @@ -19,7 +43,21 @@ class Engine: - """Engine to execute a quantum circuit""" + """ + Engine to execute a quantum circuit. + + Attributes: + _part (BasePartitioner): Partitioner for the circuit. + _sim (Simulator): Simulator object for the backend. + _circ_helper (CircuitHelper): Helper for circuit initialization. + _circ (Any): Quantum circuit to be executed. + _nq (int): Number of qubits in the circuit. + _manager (SvManager): Statevector manager. + _np (int): Number of primary qubits. + _nl (int): Number of local qubits. + _num_chunks (int): Number of chunks for statevector simulation. + + """ def __init__( self, @@ -33,6 +71,18 @@ def __init__( sv_location="disk", **backend_args ) -> None: + """ + Args: + partitioner (Optional[BasePartitioner]): Partitioner for the circuit. + manager (Optional[SvManager]): Statevector manager. + circuit (Any): Quantum circuit to be executed. + num_primary (int): Number of primary qubits. + num_local (int): Number of local qubits. + is_parallel (bool): Whether to run simulations in parallel. + backend (str): Backend simulator to use. + sv_location (str): Location to store statevectors. + **backend_args: Additional arguments for the backend simulator. + """ if isinstance(partitioner, BasePartitioner): self._part = partitioner else: @@ -76,7 +126,7 @@ def num_chunks(self): def _preprocess(self, sub_circ: QdaoCircuit, ichunk: int): """Preprocessing before running a sub-simulation Args: - sub_circ (VirtualCircuit): + sub_circ (VirtualCircuit): Sub-circuit to be processed. ichunk (int): For each sub-circuit, we need to simulate chunk by chunk, (num_chunks = 1<<(nq-np)) """ @@ -145,16 +195,14 @@ def _run(self, sub_circ: QdaoCircuit) -> None: @time_it def _initialize(self): """ - Init storage units to "|000...0>" + Initialize storage units to the state "|000...0>". """ self._manager.initialize() def run(self): """Run simulation 1. Partition the circuit into sub-circuits - 2. For each sub-circuit, run for 1<<(nq-np) times of - simulations. Each simulation will init from a - different part of statevector + 2. For each sub-circuit, run simulations `1<<(nq-np)` times. Each simulation will initialize from a different part of the statevector. """ sub_circs = self._part.run(self._circ) logging.info("Number of sub-circuits: {}".format(len(sub_circs))) diff --git a/qdao/exceptions.py b/qdao/exceptions.py index e019a30..6ff6ab7 100644 --- a/qdao/exceptions.py +++ b/qdao/exceptions.py @@ -1,5 +1,12 @@ -""" Exception for errors raised by QVM """ +""" +Exceptions Module +==================== +This module defines exceptions for errors raised by the Quantum Virtual Machine (QVM). + +Classes: + QdaoError: Base class for errors raised by QVM. +""" class QdaoError(Exception): """Base class for errors raised by QVM""" diff --git a/qdao/executor.py b/qdao/executor.py index dc61a07..d49cf52 100644 --- a/qdao/executor.py +++ b/qdao/executor.py @@ -1,3 +1,17 @@ +""" +Parallel Execution Module +========================= + +This module provides various classes for parallel execution of functions using threads and asynchronous I/O. + +Classes: + ParallelExecutor: Executes a function in parallel using threads. + BatchParallelExecutor: Executes a function in parallel using threads, processing in batches. + PoolParallelExecutor: Executes a function in parallel using a thread pool. + ConstantPoolParallelExecutor: Executes a function in parallel using a constant-sized thread pool. + AsyncIoExecutor: Executes a function in parallel using asyncio for asynchronous I/O operations. +""" + import asyncio import concurrent.futures import multiprocessing as mp @@ -8,11 +22,21 @@ class ParallelExecutor: + """ + Executes a function in parallel using threads. + + Attributes: + _func (callable): The function to execute. + _args (list): The list of arguments for the function. + """ def __init__(self, func, args) -> None: self._func = func self._args = args def execute(self): + """ + Execute the function in parallel using threads. + """ threads = [Thread(target=self._func, args=arg) for arg in self._args] for thread in threads: @@ -23,7 +47,17 @@ def execute(self): class BatchParallelExecutor: + """ + Executes a function in parallel using threads, processing in batches. + """ def execute(self, func, args_list): + """ + Execute the function in parallel using threads, processing in batches. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ cpu_cnt = mp.cpu_count() def split_list(lst, size): @@ -42,7 +76,17 @@ def split_list(lst, size): class PoolParallelExecutor: + """ + Executes a function in parallel using a thread pool. + """ def execute(self, func, args_list): + """ + Execute the function in parallel using a thread pool. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ res = [] with concurrent.futures.ThreadPoolExecutor( @@ -62,8 +106,11 @@ def execute(self, func, args_list): class ConstantPoolParallelExecutor: """ - When NQ is much larger than NL, there will be excessive overhead - Try to run thread pool group by group with each group of CPU_COUNT size + Executes a function in parallel using a constant-sized thread pool. + + When the number of qubits (NQ) is much larger than the number of layers (NL), + there will be excessive overhead. This class tries to run a thread pool group + by group with each group of CPU_COUNT size. """ CPU_CNT = mp.cpu_count() @@ -83,6 +130,13 @@ def _execute(self, func, args_list): print(f"exception! {e}") def execute(self, func, args_list): + """ + Execute the function in parallel using a constant-sized thread pool. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ res = [] def split_list(lst, size): @@ -98,9 +152,11 @@ def split_list(lst, size): class AsyncIoExecutor: """ - When NQ is much larger than NL, there will be excessive overhead - Try to run thread pool group by group with each group of CPU_COUNT size + Executes a function in parallel using asyncio for asynchronous I/O operations. + When the number of qubits (NQ) is much larger than the number of layers (NL), + there will be excessive overhead. This class tries to run thread pool group + by group with each group of CPU_COUNT size. """ CPU_CNT = mp.cpu_count() @@ -117,4 +173,11 @@ async def _execute_one_batch(self, func, args_list): results = await asyncio.gather(*tasks) def execute(self, func, args_list): + """ + Execute the function in parallel using asyncio. + + Args: + func (callable): The function to execute. + args_list (list): The list of argument lists for the function. + """ asyncio.run(self._execute_one_batch(func, args_list)) diff --git a/qdao/manager.py b/qdao/manager.py index 9f6ac25..1d5ae8b 100644 --- a/qdao/manager.py +++ b/qdao/manager.py @@ -1,3 +1,36 @@ +""" +Statevector Manager Module +========================== + +This module provides an `SvManager` class to manage the statevector data access +and storage for quantum circuit simulations. The statevectors can be stored in +memory or on disk, and the class supports both serial and parallel execution +for initializing, loading, and storing statevector chunks. + +Modules: +-------- + +- concurrent.futures: Provides a high-level interface for asynchronously executing callables. +- logging: Provides a flexible framework for emitting log messages from Python programs. +- multiprocessing: Supports spawning processes using an API similar to the threading module. +- os: Provides a portable way of using operating system dependent functionality. +- threading: Constructs higher-level threading interfaces. +- typing: Provides runtime support for type hints. +- numpy: Provides support for large, multi-dimensional arrays and matrices. +- qdao.executor: Contains executor classes for parallel execution. +- qdao.util: Provides utility functions for safe import and file name generation. + +Classes: +-------- + +- SvManager: Manages statevector storage and retrieval. + +Attributes: +----------- + +- print_statistics: A function to print execution statistics. +- time_it: A decorator to measure the execution time of methods. +""" import concurrent.futures import logging import multiprocessing as mp @@ -21,7 +54,24 @@ class SvManager: - """Statevector data access manager""" + """ + Statevector data access manager. + + This class handles the initialization, loading, and storing of statevector chunks + for quantum circuit simulations. Statevectors can be stored in memory or on disk, + and the operations can be performed either serially or in parallel. + + Attributes: + _nq (int): Number of qubits in the target circuit. + _np (int): Number of qubits in primary storage (host memory). + _nl (int): Number of qubits in secondary storage (disk). + _chunk_idx (int): Index of the current chunk. + _chunk (np.ndarray): Current chunk of the statevector. + _is_parallel (bool): Indicates if operations should be parallelized. + _executor (BatchParallelExecutor): Executor for parallel operations. + _global_sv (List[np.ndarray]): List of statevector chunks in memory. + _sv_location (str): Location of statevector storage ('memory' or 'disk'). + """ def __init__( self, @@ -32,11 +82,14 @@ def __init__( sv_location="disk", ) -> None: """ + Initializes the SvManager with the specified parameters. + Args: - num_qubits (int): Number of qubits in the target circuit - num_primary (int): Number of qubits that reside in primary storage (i.e., host memory) - num_local (int): Number of qubits that reside in secondary storage (i.e., disk). - Note that this defines the size of minimum storage unit. + num_qubits (int): Number of qubits in the target circuit. + num_primary (int): Number of qubits in primary storage. + num_local (int): Number of qubits in secondary storage. + is_parallel (bool): Indicates if operations should be parallelized. + sv_location (str): Location of statevector storage ('memory' or 'disk'). """ self._nq, self._np, self._nl = num_qubits, num_primary, num_local self._chunk_idx = 0 diff --git a/qdao/qiskit/annotated_operation.py b/qdao/qiskit/annotated_operation.py index 803baa6..f2351bd 100644 --- a/qdao/qiskit/annotated_operation.py +++ b/qdao/qiskit/annotated_operation.py @@ -10,7 +10,30 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Annotated Operations.""" +""" +Annotated Operations +==================== + +This module defines the `AnnotatedOperation` class and various modifier classes +used to create annotated operations in Qiskit. Annotated operations allow the +addition of modifiers to base operations, enabling more abstract representations +and optimizations during transpilation. + +Classes: +-------- + +- Modifier: Base class for all modifiers. +- InverseModifier: Modifier to specify that the operation is inverted. +- ControlModifier: Modifier to specify that the operation is controlled. +- PowerModifier: Modifier to specify that the operation is raised to a power. +- AnnotatedOperation: Class representing an annotated operation. + +Functions: +---------- + +- _canonicalize_modifiers: Returns the canonical representative of the modifier list. + +""" from __future__ import annotations @@ -84,8 +107,8 @@ def __init__(self, base_op: Operation, modifiers: Union[Modifier, List[Modifier] that is immediately followed by its inverse. Args: - base_op: base operation being modified - modifiers: ordered list of modifiers. Supported modifiers include + base_op (Operation): base operation being modified + modifiers (Union[Modifier, List[Modifier]]): ordered list of modifiers. Supported modifiers include ``InverseModifier``, ``ControlModifier`` and ``PowerModifier``. Examples:: @@ -169,14 +192,14 @@ def control( Implemented as an annotated operation, see :class:`.AnnotatedOperation`. Args: - num_ctrl_qubits: number of controls to add to gate (default: ``1``) - label: ignored (used for consistency with other control methods) - ctrl_state: The control state in decimal or as a bitstring + num_ctrl_qubits (int): number of controls to add to gate (default: ``1``) + label (str | None): ignored (used for consistency with other control methods) + ctrl_state (int | str | None): The control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated: ignored (used for consistency with other control methods) + annotated (bool): ignored (used for consistency with other control methods) Returns: - Controlled version of the given operation. + AnnotatedOperation: Controlled version of the given operation. """ # pylint: disable=unused-argument extended_modifiers = self.modifiers.copy() @@ -192,10 +215,10 @@ def inverse(self, annotated: bool = True): Implemented as an annotated operation, see :class:`.AnnotatedOperation`. Args: - annotated: ignored (used for consistency with other inverse methods) + annotated (bool): ignored (used for consistency with other inverse methods) Returns: - Inverse version of the given operation. + AnnotatedOperation: Inverse version of the given operation. """ # pylint: disable=unused-argument extended_modifiers = self.modifiers.copy() @@ -211,6 +234,12 @@ def _canonicalize_modifiers(modifiers): of control qubits / control state and the total power. The InverseModifier will be present if total power is negative, whereas the power modifier will be present only with positive powers different from 1. + + Args: + modifiers (List[Modifier]): List of modifiers to canonicalize. + + Returns: + List[Modifier]: Canonical list of modifiers. """ power = 1 num_ctrl_qubits = 0 diff --git a/qdao/qiskit/circuit.py b/qdao/qiskit/circuit.py index a6253c6..36d0df3 100644 --- a/qdao/qiskit/circuit.py +++ b/qdao/qiskit/circuit.py @@ -11,6 +11,14 @@ class QiskitCircuitWrapper(BaseCircWrapper): + """ + Wrapper class for Qiskit QuantumCircuit. + + Provides additional functionalities and interface for interaction with QDAO. + + Args: + circ (Optional[QuantumCircuit]): Qiskit QuantumCircuit instance. + """ def __init__(self, circ: Optional[QuantumCircuit] = None) -> None: self._circ = circ or None @@ -40,6 +48,9 @@ def get_instr_qubits(self, instruction: CircuitInstruction): def init_circ_from_sv(self, sv: np.ndarray): """Initialize qiskit QuantumCircuit from given statevector + + Args: + sv (np.ndarray): Statevector to initialize the circuit with. Comments: 1. Currently qiskit QuantumCircuit.initialize will @@ -69,9 +80,12 @@ def gen_sub_circ( instructions Args: - instrs (List[CircuitInstruction]): A list of instructions - Return: - QdaoCircuit + instrs (List[CircuitInstruction]): A list of instructions. + num_local (int): Number of local qubits. + num_primary (int): Number of primary qubits. + + Returns: + QdaoCircuit: Generated sub-circuit. """ if not isinstance(self._circ, QuantumCircuit): raise ValueError("Please set self._circ") diff --git a/qdao/qiskit/gate.py b/qdao/qiskit/gate.py index c4a3138..d5304de 100644 --- a/qdao/qiskit/gate.py +++ b/qdao/qiskit/gate.py @@ -10,7 +10,50 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Unitary gate.""" +""" +Unitary Gate Module +==================== + +This module provides a `Gate` class to represent unitary gates in quantum circuits. +It includes methods for matrix conversion, exponentiation, control, and argument broadcasting. + +Modules: +-------- +- qiskit.circuit.exceptions: Contains exceptions related to quantum circuits. +- qiskit.circuit.parameterexpression: Handles parameter expressions in quantum circuits. +- .annotated_operation: Provides classes for annotated operations. +- .instruction: Defines the base `Instruction` class. + +Classes: +-------- +- Gate: Represents a unitary gate in a quantum circuit. + +Methods: +-------- +- __init__(self, name: str, num_qubits: int, params: list, label: str | None = None, duration: Any = None, unit: str = "dt") + Create a new gate. +- to_matrix(self) -> np.ndarray + Return a Numpy.array for the gate unitary matrix. +- power(self, exponent: float) + Creates a unitary gate as `gate^exponent`. +- __pow__(self, exponent: float) -> "Gate" + Allows exponentiation using the `**` operator. +- _return_repeat(self, exponent: float) -> "Gate" + Returns a repeated version of the gate. +- control(self, num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: int | str | None = None, annotated: bool = False) + Return the controlled version of itself. +- _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]] + Expands a single argument for broadcasting. +- _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, list]] + Expands two arguments for broadcasting. +- _broadcast_3_or_more_args(qargs: list) -> Iterator[tuple[list, list]] + Expands three or more arguments for broadcasting. +- broadcast_arguments(self, qargs: list, cargs: list) -> Iterable[tuple[list, list]] + Validation and handling of the arguments and their relationships. +- validate_parameter(self, parameter: Any) + Gate parameters should be int, float, or ParameterExpression. + +""" from __future__ import annotations @@ -39,10 +82,12 @@ def __init__( """Create a new gate. Args: - name: The Qobj name of the gate. - num_qubits: The number of qubits the gate acts on. - params: A list of parameters. - label: An optional label for the gate. + name (str): The Qobj name of the gate. + num_qubits (int): The number of qubits the gate acts on. + params (list): A list of parameters. + label (str | None): An optional label for the gate. + duration (Any): The duration of the gate. + unit (str): The unit of the gate duration. """ self.definition = None super().__init__( @@ -109,13 +154,13 @@ def control( Implemented either as a controlled gate (ref. :class:`.ControlledGate`) or as an annotated operation (ref. :class:`.AnnotatedOperation`). - Args: - num_ctrl_qubits: number of controls to add to gate (default: ``1``) - label: optional gate label. Ignored if implemented as an annotated + Args: + num_ctrl_qubits (int): number of controls to add to gate (default: ``1``) + label (str | None): optional gate label. Ignored if implemented as an annotated operation. - ctrl_state: the control state in decimal or as a bitstring + ctrl_state (int | str | None): the control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated: indicates whether the controlled gate can be implemented + annotated (bool): indicates whether the controlled gate can be implemented as an annotated gate. Returns: @@ -139,7 +184,13 @@ def control( @staticmethod def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: """Expands a single argument. - + + Args: + qarg (list): List of qubit arguments. + + Returns: + Iterator[tuple[list, list]]: Iterator of expanded arguments. + For example: [q[0], q[1]] -> [q[0]], [q[1]] """ # [q[0], q[1]] -> [q[0]] @@ -149,6 +200,18 @@ def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: @staticmethod def _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, list]]: + """Expands two arguments for broadcasting. + + Args: + qarg0 (list): First list of qubit arguments. + qarg1 (list): Second list of qubit arguments. + + Returns: + Iterator[tuple[list, list]]: Iterator of expanded arguments. + + Raises: + CircuitError: If the arguments cannot be combined. + """ if len(qarg0) == len(qarg1): # [[q[0], q[1]], [r[0], r[1]]] -> [q[0], r[0]] # -> [q[1], r[1]] @@ -171,6 +234,18 @@ def _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, lis @staticmethod def _broadcast_3_or_more_args(qargs: list) -> Iterator[tuple[list, list]]: + """Expands three or more arguments for broadcasting. + + Args: + qargs (list): List of lists of qubit arguments. + + Returns: + Iterator[tuple[list, list]]: Iterator of expanded arguments. + + Raises: + CircuitError: If the arguments cannot be combined. + """ + if all(len(qarg) == len(qargs[0]) for qarg in qargs): for arg in zip(*qargs): yield list(arg), [] @@ -208,15 +283,16 @@ def broadcast_arguments( [q[0], q[1]], [r[0], r[1]], ...] -> [q[0], r[0], ...], [q[1], r[1], ...] Args: - qargs: List of quantum bit arguments. - cargs: List of classical bit arguments. + qargs (list): List of quantum bit arguments. + cargs (list): List of classical bit arguments. Returns: - A tuple with single arguments. + Iterable[tuple[list, list]]: A tuple with single arguments. Raises: CircuitError: If the input is not valid. For example, the number of arguments does not match the gate expectation. + """ if len(qargs) != self.num_qubits or cargs: raise CircuitError( @@ -241,7 +317,14 @@ def broadcast_arguments( raise CircuitError("This gate cannot handle %i arguments" % len(qargs)) def validate_parameter(self, parameter): - """Gate parameters should be int, float, or ParameterExpression""" + """Gate parameters should be int, float, or ParameterExpression + + Args: + parameter (Any): Parameter to validate. + + Raises: + CircuitError: If the parameter is invalid. + """ if isinstance(parameter, ParameterExpression): if len(parameter.parameters) > 0: return ( diff --git a/qdao/qiskit/instruction.py b/qdao/qiskit/instruction.py index ecf8cea..560a378 100644 --- a/qdao/qiskit/instruction.py +++ b/qdao/qiskit/instruction.py @@ -11,7 +11,10 @@ # that they have been altered from the originals. """ -A generic quantum instruction. +Qiskit Instruction Module +========================== + +This module contains the definition and functionality of a generic quantum instruction. Instructions can be implementable on hardware (u, cx, etc.) or in simulation (snapshot, noise, etc.). diff --git a/qdao/qiskit/simulator.py b/qdao/qiskit/simulator.py index 9289870..84fe6d2 100644 --- a/qdao/qiskit/simulator.py +++ b/qdao/qiskit/simulator.py @@ -1,3 +1,24 @@ +""" +Qiskit Simulator Module +======================== + +This module provides a `QiskitSimulator` class to interface with Qiskit's Aer simulator and other +potential simulators. It includes methods for initialization and running simulations. + +Modules: +-------- +- logging: Provides logging capabilities. +- typing: Includes the Optional type hint. +- numpy: Handles numerical operations and arrays. +- qiskit_aer: Provides access to Qiskit's Aer simulator. +- qdao.exceptions: Contains custom exceptions for the quantum DAO (Qdao) module. + +Classes: +-------- +- QiskitSimulator: Interfaces with Qiskit simulators for running quantum circuit simulations. + + +""" import logging from typing import Optional @@ -7,12 +28,36 @@ class QiskitSimulator: + """ + A class to interface with Qiskit's Aer simulator and other potential simulators. + + Attributes: + ----------- + _sim : Backend + The backend simulator used for running quantum simulations. + + Methods: + -------- + __init__(self, provider: Optional[str] = None, fusion: Optional[bool] = False, device: str = "CPU") -> None + Initializes the simulator with the specified provider, fusion option, and device type. + + run(self, simobj) -> np.ndarray + Runs a simulation on the provided simulation object and returns the resulting state vector. + """ def __init__( self, provider: Optional[str] = None, fusion: Optional[bool] = False, device: str = "CPU", ) -> None: + """ + Initialize the QiskitSimulator. + + Args: + provider (Optional[str]): The simulator provider to use (e.g., 'ddsim'). Defaults to None. + fusion (Optional[bool]): Whether to enable fusion optimizations. Defaults to False. + device (str): The device to use for simulation ('CPU' or 'GPU'). Defaults to 'CPU'. + """ if provider: if provider == "ddsim": from mqt import ddsim @@ -26,6 +71,18 @@ def __init__( self._sim.set_options(device=device) def run(self, simobj) -> np.ndarray: + """ + Run a simulation on the provided simulation object. + + Args: + simobj: The simulation object containing the quantum circuit to simulate. + + Returns: + np.ndarray: The resulting state vector from the simulation. + + Raises: + QdaoError: If the simulation fails. + """ res = self._sim.run(simobj.circ).result() if not res.success: raise QdaoError( diff --git a/qdao/qiskit/utils.py b/qdao/qiskit/utils.py index 81a7cb6..a976c93 100644 --- a/qdao/qiskit/utils.py +++ b/qdao/qiskit/utils.py @@ -10,7 +10,23 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Utility functions for generating random circuits.""" +""" +Utility Functions for Generating Random Circuits +================================================= + +This module provides utility functions for generating random quantum circuits. + +Modules: +-------- +- numpy: Handles numerical operations and arrays. +- qiskit.circuit: Provides the QuantumCircuit, QuantumRegister, ClassicalRegister, and exceptions for quantum circuits. +- qiskit.circuit.library.standard_gates: Contains standard gates for quantum circuits. + +Functions: +---------- +- random_circuit(num_qubits: int, depth: int, max_operands: int = 3, measure: bool = False, conditional: bool = False, reset: bool = False, seed: int = None) -> QuantumCircuit + Generate a random quantum circuit with the specified parameters. +""" import numpy as np from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister, Reset @@ -66,20 +82,20 @@ def random_circuit( circ = random_circuit(2, 2, measure=True) circ.draw(output='mpl') - Args: - num_qubits (int): number of quantum wires - depth (int): layers of operations (i.e. critical path length) - max_operands (int): maximum operands of each gate (between 1 and 3) - measure (bool): if True, measure all qubits at the end - conditional (bool): if True, insert middle measurements and conditionals - reset (bool): if True, insert middle resets - seed (int): sets random seed (optional) + Args: + num_qubits (int): Number of quantum wires. + depth (int): Layers of operations (i.e. critical path length). + max_operands (int): Maximum operands of each gate (between 1 and 3). Defaults to 3. + measure (bool): If True, measure all qubits at the end. Defaults to False. + conditional (bool): If True, insert middle measurements and conditionals. Defaults to False. + reset (bool): If True, insert middle resets. Defaults to False. + seed (int): Sets random seed (optional). Defaults to None. Returns: - QuantumCircuit: constructed circuit + QuantumCircuit: Constructed random quantum circuit. Raises: - CircuitError: when invalid options given + CircuitError: When invalid options are given. """ if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") diff --git a/qdao/qsim/circuit.py b/qdao/qsim/circuit.py index 4ef3b8c..2b13a4a 100644 --- a/qdao/qsim/circuit.py +++ b/qdao/qsim/circuit.py @@ -1,3 +1,30 @@ +""" +Qsim Circuit Helper Module +========================== + +This module provides the `QsimCircuitHelper` class, which is used to manage and manipulate +quantum circuits specific to the Qsim framework. The helper class facilitates various +operations such as initializing circuits from state vectors, generating sub-circuits, and +retrieving instructions and qubit mappings. + +Modules: +-------- + +- qdao.circuit: Contains the QdaoCircuit class for representing quantum circuits. +- cirq.circuits: Contains the Circuit class for representing quantum circuits in Cirq. +- cirq.ops.gate_operation: Contains the GateOperation class for representing gate operations. + +Classes: +-------- + +- QsimCircuitHelper: Helper class for managing Qsim quantum circuits. + +Attributes: +----------- + +- Circuit: The main class for representing a quantum circuit in Cirq. +- GateOperation: The class for representing gate operations in Cirq. +""" import copy import logging from typing import List, Optional @@ -26,7 +53,31 @@ class QsimCircuitHelper: + """ + Helper class for managing and manipulating Qsim quantum circuits. + + This class provides methods for initializing circuits from state vectors, generating + sub-circuits based on a list of instructions, and retrieving instructions and qubit + mappings from a given circuit. + + Attributes: + _circ (Circuit): The quantum circuit object. + + Methods: + circ: Returns or sets the quantum circuit object. + num_qubits: Returns the number of qubits in the circuit. + instructions: Returns the list of instructions in the circuit. + get_instr_qubits: Returns the qubits involved in a given instruction. + init_circ_from_sv: Initializes a circuit from a given state vector. + gen_sub_circ: Generates a sub-circuit based on a list of instructions. + """ def __init__(self, circ: Optional[Circuit] = None) -> None: + """ + Initializes the QsimCircuitHelper with an optional quantum circuit. + + Args: + circ (Circuit, optional): The quantum circuit object. + """ self._circ = circ or None self._circ.num = 0 self._circ.itrs = [] diff --git a/qdao/quafu/circuit.py b/qdao/quafu/circuit.py index bbb2586..a8ad608 100644 --- a/qdao/quafu/circuit.py +++ b/qdao/quafu/circuit.py @@ -1,3 +1,31 @@ +""" +Quafu Circuit Module +=========================== + +This module provides the `QuafuCircuitHelper` class, which is used to manage and manipulate +quantum circuits specific to the Quafu framework. The helper class facilitates various +operations such as initializing circuits from state vectors, generating sub-circuits, and +retrieving instructions and qubit mappings. + +Modules: +-------- + +- qdao.base_circuit_wrapper: Contains the base class for circuit wrappers. +- quafu.circuits.quantum_circuit: Contains the QuantumCircuit and related classes. + +Classes: +-------- + +- QuafuCircuitHelper: Helper class for managing Quafu quantum circuits. + +Attributes: +----------- + +- QuantumCircuit: The main class for representing a quantum circuit. +- QuantumGate: The base class for quantum gates. +- SingleQubitGate: Represents single-qubit gates. +- ControlledGate: Represents controlled gates. +""" import copy import logging from typing import List, Optional @@ -13,6 +41,24 @@ class QuafuCircuitHelper(BaseCircWrapper): + """ + Helper class for managing and manipulating Quafu quantum circuits. + + This class provides methods for initializing circuits from state vectors, generating + sub-circuits based on a list of instructions, and retrieving instructions and qubit + mappings from a given circuit. + + Attributes: + _circ (QuantumCircuit): The quantum circuit object. + + Methods: + circ: Returns or sets the quantum circuit object. + num_qubits: Returns the number of qubits in the circuit. + instructions: Returns the list of instructions in the circuit. + get_instr_qubits: Returns the qubits involved in a given instruction. + init_circ_from_sv: Initializes a circuit from a given state vector. + gen_sub_circ: Generates a sub-circuit based on a list of instructions. + """ def __init__(self, circ: Optional[QuantumCircuit] = None) -> None: self._circ = circ or None @@ -56,14 +102,18 @@ def init_circ_from_sv(self, sv: np.ndarray): return QdaoSimObj(sv, self._circ) def gen_sub_circ(self, instrs: List[QuantumGate], num_local: int, num_primary: int): - """Generate a sub circuit based on a list of circuit instructions - We assume there's no conditional instructions and no measurement - instructions + """ + Generates a sub-circuit based on a list of circuit instructions. + + Assumes there are no conditional instructions and no measurement instructions. Args: - instrs (List[QuantumGate]): A list of instructions - Return: - QdaoCircuit + instrs (List[QuantumGate]): A list of instructions. + num_local (int): The number of local qubits. + num_primary (int): The number of primary qubits. + + Returns: + QdaoCircuit: The generated sub-circuit. """ if not isinstance(self._circ, QuantumCircuit): raise ValueError("Please set self._circ") diff --git a/qdao/simulator.py b/qdao/simulator.py index b5229fb..851312b 100644 --- a/qdao/simulator.py +++ b/qdao/simulator.py @@ -1,25 +1,71 @@ -"""Simulator provider""" +""" +Simulator Provider Module +========================= + +This module provides the `SimulatorProvider` class and associated objects for managing +and interacting with different quantum simulators. The simulators can be accessed through +a unified interface, making it easy to switch between different backend providers. + +Modules: +-------- + +- qdao.qiskit.simulator: Contains the QiskitSimulator class. +- qdao.quafu.simulator: Contains the QuafuSimulator class. + +Classes: +-------- + +- QdaoSimObj: Represents a simulation object containing the circuit and run options. +- SimulatorProvider: Provides access to different quantum simulators based on backend name. + +Attributes: +----------- + +- SIMS: A dictionary mapping backend names to their respective simulator classes. +""" from qdao.qiskit.simulator import QiskitSimulator from qdao.quafu.simulator import QuafuSimulator class QdaoSimObj: + """ + Simulation object for managing quantum circuits and run options. + + This class encapsulates the quantum circuit and any additional options required + to run the simulation. + + Attributes: + _objs (tuple): Tuple containing the simulation objects. + _circ (Any): The quantum circuit object. + _run_options (dict): Options for running the simulation. + """ + def __init__(self, *objs, **options) -> None: + """ + Initializes the QdaoSimObj with the provided objects and options. + + Args: + objs (tuple): Simulation objects. + options (dict): Run options for the simulation. + """ self._objs = objs self._circ = objs[-1] self._run_options = options @property def circ(self): + """Returns the quantum circuit object.""" return self._circ @property def objs(self): + """Returns the simulation objects.""" return self._objs @property def run_options(self): + """Returns the run options for the simulation.""" return self._run_options @@ -27,6 +73,29 @@ def run_options(self): class SimulatorProvider: + """ + Provides access to different quantum simulators. + + This class allows the selection and initialization of a quantum simulator + based on the specified backend name. + + Methods: + get_simulator: Returns an instance of the specified simulator backend. + + Attributes: + SIMS (dict): Dictionary mapping backend names to simulator classes. + """ + @classmethod def get_simulator(cls, backend_name: str, **kwargs): + """ + Returns an instance of the specified simulator backend. + + Args: + backend_name (str): The name of the backend simulator. + kwargs (dict): Additional arguments for the simulator. + + Returns: + Simulator: An instance of the requested simulator backend. + """ return SIMS[backend_name](**kwargs) From 58df6d2b8d8ad8154ed625ead743a6f8247f83cb Mon Sep 17 00:00:00 2001 From: raunakkumarsingh <35128126+raunakkumarsingh@users.noreply.github.com> Date: Sun, 4 Aug 2024 14:13:02 +0530 Subject: [PATCH 3/3] comment issue resolved --- qdao/circuit.py | 12 +-- qdao/executor.py | 4 +- qdao/qiskit/annotated_operation.py | 51 +++---------- qdao/qiskit/gate.py | 115 ++++------------------------- qdao/qiskit/instruction.py | 7 +- qdao/qiskit/simulator.py | 59 +-------------- qdao/qiskit/utils.py | 40 +++------- qdao/simulator.py | 6 -- 8 files changed, 50 insertions(+), 244 deletions(-) diff --git a/qdao/circuit.py b/qdao/circuit.py index 38483e3..f370b96 100644 --- a/qdao/circuit.py +++ b/qdao/circuit.py @@ -56,8 +56,8 @@ class BasePartitioner: Base class for circuit partitioning. Attributes: - np (int): Number of partitions. - nl (int): Number of layers. + np (int): Number of primary qubits. + nl (int): Number of local qubits. backend (str): The backend used for the partitioning process. """ @@ -68,22 +68,22 @@ def __init__(self, np=4, nl=2, backend="qiskit") -> None: @property def np(self): - """Gets the number of partitions.""" + """Gets the number of primary qubits.""" return self._np @np.setter def np(self, n): - """Sets the number of partitions.""" + """Sets the number of primary qubits.""" self._np = n @property def nl(self): - """Sets the number of layers.""" + """Sets the number of local qubits.""" return self._nl @nl.setter def nl(self, n): - """Sets the number of layers.""" + """Sets the number of local qubits.""" self._nl = n def run(self, circuit: Any) -> List[QdaoCircuit]: diff --git a/qdao/executor.py b/qdao/executor.py index d49cf52..d0d5da7 100644 --- a/qdao/executor.py +++ b/qdao/executor.py @@ -108,7 +108,7 @@ class ConstantPoolParallelExecutor: """ Executes a function in parallel using a constant-sized thread pool. - When the number of qubits (NQ) is much larger than the number of layers (NL), + When the number of qubits (NQ) is much larger than the number of local qubits (NL), there will be excessive overhead. This class tries to run a thread pool group by group with each group of CPU_COUNT size. """ @@ -154,7 +154,7 @@ class AsyncIoExecutor: """ Executes a function in parallel using asyncio for asynchronous I/O operations. - When the number of qubits (NQ) is much larger than the number of layers (NL), + When the number of qubits (NQ) is much larger than the number of local qubits (NL), there will be excessive overhead. This class tries to run thread pool group by group with each group of CPU_COUNT size. """ diff --git a/qdao/qiskit/annotated_operation.py b/qdao/qiskit/annotated_operation.py index f2351bd..57d7e29 100644 --- a/qdao/qiskit/annotated_operation.py +++ b/qdao/qiskit/annotated_operation.py @@ -10,30 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Annotated Operations -==================== - -This module defines the `AnnotatedOperation` class and various modifier classes -used to create annotated operations in Qiskit. Annotated operations allow the -addition of modifiers to base operations, enabling more abstract representations -and optimizations during transpilation. - -Classes: --------- - -- Modifier: Base class for all modifiers. -- InverseModifier: Modifier to specify that the operation is inverted. -- ControlModifier: Modifier to specify that the operation is controlled. -- PowerModifier: Modifier to specify that the operation is raised to a power. -- AnnotatedOperation: Class representing an annotated operation. - -Functions: ----------- - -- _canonicalize_modifiers: Returns the canonical representative of the modifier list. - -""" +"""Annotated Operations.""" from __future__ import annotations @@ -107,8 +84,8 @@ def __init__(self, base_op: Operation, modifiers: Union[Modifier, List[Modifier] that is immediately followed by its inverse. Args: - base_op (Operation): base operation being modified - modifiers (Union[Modifier, List[Modifier]]): ordered list of modifiers. Supported modifiers include + base_op: base operation being modified + modifiers: ordered list of modifiers. Supported modifiers include ``InverseModifier``, ``ControlModifier`` and ``PowerModifier``. Examples:: @@ -192,14 +169,14 @@ def control( Implemented as an annotated operation, see :class:`.AnnotatedOperation`. Args: - num_ctrl_qubits (int): number of controls to add to gate (default: ``1``) - label (str | None): ignored (used for consistency with other control methods) - ctrl_state (int | str | None): The control state in decimal or as a bitstring + num_ctrl_qubits: number of controls to add to gate (default: ``1``) + label: ignored (used for consistency with other control methods) + ctrl_state: The control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated (bool): ignored (used for consistency with other control methods) + annotated: ignored (used for consistency with other control methods) Returns: - AnnotatedOperation: Controlled version of the given operation. + Controlled version of the given operation. """ # pylint: disable=unused-argument extended_modifiers = self.modifiers.copy() @@ -215,10 +192,10 @@ def inverse(self, annotated: bool = True): Implemented as an annotated operation, see :class:`.AnnotatedOperation`. Args: - annotated (bool): ignored (used for consistency with other inverse methods) + annotated: ignored (used for consistency with other inverse methods) Returns: - AnnotatedOperation: Inverse version of the given operation. + Inverse version of the given operation. """ # pylint: disable=unused-argument extended_modifiers = self.modifiers.copy() @@ -234,12 +211,6 @@ def _canonicalize_modifiers(modifiers): of control qubits / control state and the total power. The InverseModifier will be present if total power is negative, whereas the power modifier will be present only with positive powers different from 1. - - Args: - modifiers (List[Modifier]): List of modifiers to canonicalize. - - Returns: - List[Modifier]: Canonical list of modifiers. """ power = 1 num_ctrl_qubits = 0 @@ -266,4 +237,4 @@ def _canonicalize_modifiers(modifiers): if num_ctrl_qubits > 0: canonical_modifiers.append(ControlModifier(num_ctrl_qubits, ctrl_state)) - return canonical_modifiers + return canonical_modifiers \ No newline at end of file diff --git a/qdao/qiskit/gate.py b/qdao/qiskit/gate.py index d5304de..00a74f1 100644 --- a/qdao/qiskit/gate.py +++ b/qdao/qiskit/gate.py @@ -10,50 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Unitary Gate Module -==================== - -This module provides a `Gate` class to represent unitary gates in quantum circuits. -It includes methods for matrix conversion, exponentiation, control, and argument broadcasting. - -Modules: --------- -- qiskit.circuit.exceptions: Contains exceptions related to quantum circuits. -- qiskit.circuit.parameterexpression: Handles parameter expressions in quantum circuits. -- .annotated_operation: Provides classes for annotated operations. -- .instruction: Defines the base `Instruction` class. - -Classes: --------- -- Gate: Represents a unitary gate in a quantum circuit. - -Methods: --------- -- __init__(self, name: str, num_qubits: int, params: list, label: str | None = None, duration: Any = None, unit: str = "dt") - Create a new gate. -- to_matrix(self) -> np.ndarray - Return a Numpy.array for the gate unitary matrix. -- power(self, exponent: float) - Creates a unitary gate as `gate^exponent`. -- __pow__(self, exponent: float) -> "Gate" - Allows exponentiation using the `**` operator. -- _return_repeat(self, exponent: float) -> "Gate" - Returns a repeated version of the gate. -- control(self, num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: int | str | None = None, annotated: bool = False) - Return the controlled version of itself. -- _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]] - Expands a single argument for broadcasting. -- _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, list]] - Expands two arguments for broadcasting. -- _broadcast_3_or_more_args(qargs: list) -> Iterator[tuple[list, list]] - Expands three or more arguments for broadcasting. -- broadcast_arguments(self, qargs: list, cargs: list) -> Iterable[tuple[list, list]] - Validation and handling of the arguments and their relationships. -- validate_parameter(self, parameter: Any) - Gate parameters should be int, float, or ParameterExpression. - -""" +"""Unitary gate.""" from __future__ import annotations @@ -82,12 +39,10 @@ def __init__( """Create a new gate. Args: - name (str): The Qobj name of the gate. - num_qubits (int): The number of qubits the gate acts on. - params (list): A list of parameters. - label (str | None): An optional label for the gate. - duration (Any): The duration of the gate. - unit (str): The unit of the gate duration. + name: The Qobj name of the gate. + num_qubits: The number of qubits the gate acts on. + params: A list of parameters. + label: An optional label for the gate. """ self.definition = None super().__init__( @@ -154,13 +109,13 @@ def control( Implemented either as a controlled gate (ref. :class:`.ControlledGate`) or as an annotated operation (ref. :class:`.AnnotatedOperation`). - Args: - num_ctrl_qubits (int): number of controls to add to gate (default: ``1``) - label (str | None): optional gate label. Ignored if implemented as an annotated + Args: + num_ctrl_qubits: number of controls to add to gate (default: ``1``) + label: optional gate label. Ignored if implemented as an annotated operation. - ctrl_state (int | str | None): the control state in decimal or as a bitstring + ctrl_state: the control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated (bool): indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate can be implemented as an annotated gate. Returns: @@ -184,13 +139,7 @@ def control( @staticmethod def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: """Expands a single argument. - - Args: - qarg (list): List of qubit arguments. - - Returns: - Iterator[tuple[list, list]]: Iterator of expanded arguments. - + For example: [q[0], q[1]] -> [q[0]], [q[1]] """ # [q[0], q[1]] -> [q[0]] @@ -200,18 +149,6 @@ def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: @staticmethod def _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, list]]: - """Expands two arguments for broadcasting. - - Args: - qarg0 (list): First list of qubit arguments. - qarg1 (list): Second list of qubit arguments. - - Returns: - Iterator[tuple[list, list]]: Iterator of expanded arguments. - - Raises: - CircuitError: If the arguments cannot be combined. - """ if len(qarg0) == len(qarg1): # [[q[0], q[1]], [r[0], r[1]]] -> [q[0], r[0]] # -> [q[1], r[1]] @@ -234,18 +171,6 @@ def _broadcast_2_arguments(qarg0: list, qarg1: list) -> Iterator[tuple[list, lis @staticmethod def _broadcast_3_or_more_args(qargs: list) -> Iterator[tuple[list, list]]: - """Expands three or more arguments for broadcasting. - - Args: - qargs (list): List of lists of qubit arguments. - - Returns: - Iterator[tuple[list, list]]: Iterator of expanded arguments. - - Raises: - CircuitError: If the arguments cannot be combined. - """ - if all(len(qarg) == len(qargs[0]) for qarg in qargs): for arg in zip(*qargs): yield list(arg), [] @@ -283,16 +208,15 @@ def broadcast_arguments( [q[0], q[1]], [r[0], r[1]], ...] -> [q[0], r[0], ...], [q[1], r[1], ...] Args: - qargs (list): List of quantum bit arguments. - cargs (list): List of classical bit arguments. + qargs: List of quantum bit arguments. + cargs: List of classical bit arguments. Returns: - Iterable[tuple[list, list]]: A tuple with single arguments. + A tuple with single arguments. Raises: CircuitError: If the input is not valid. For example, the number of arguments does not match the gate expectation. - """ if len(qargs) != self.num_qubits or cargs: raise CircuitError( @@ -317,14 +241,7 @@ def broadcast_arguments( raise CircuitError("This gate cannot handle %i arguments" % len(qargs)) def validate_parameter(self, parameter): - """Gate parameters should be int, float, or ParameterExpression - - Args: - parameter (Any): Parameter to validate. - - Raises: - CircuitError: If the parameter is invalid. - """ + """Gate parameters should be int, float, or ParameterExpression""" if isinstance(parameter, ParameterExpression): if len(parameter.parameters) > 0: return ( @@ -341,4 +258,4 @@ def validate_parameter(self, parameter): else: raise CircuitError( f"Invalid param type {type(parameter)} for gate {self.name}." - ) + ) \ No newline at end of file diff --git a/qdao/qiskit/instruction.py b/qdao/qiskit/instruction.py index 560a378..c191eaa 100644 --- a/qdao/qiskit/instruction.py +++ b/qdao/qiskit/instruction.py @@ -11,10 +11,7 @@ # that they have been altered from the originals. """ -Qiskit Instruction Module -========================== - -This module contains the definition and functionality of a generic quantum instruction. +A generic quantum instruction. Instructions can be implementable on hardware (u, cx, etc.) or in simulation (snapshot, noise, etc.). @@ -681,4 +678,4 @@ def _compare_parameters(self, other): except TypeError: if x != y: return False - return True + return True \ No newline at end of file diff --git a/qdao/qiskit/simulator.py b/qdao/qiskit/simulator.py index 84fe6d2..7eb4679 100644 --- a/qdao/qiskit/simulator.py +++ b/qdao/qiskit/simulator.py @@ -1,24 +1,3 @@ -""" -Qiskit Simulator Module -======================== - -This module provides a `QiskitSimulator` class to interface with Qiskit's Aer simulator and other -potential simulators. It includes methods for initialization and running simulations. - -Modules: --------- -- logging: Provides logging capabilities. -- typing: Includes the Optional type hint. -- numpy: Handles numerical operations and arrays. -- qiskit_aer: Provides access to Qiskit's Aer simulator. -- qdao.exceptions: Contains custom exceptions for the quantum DAO (Qdao) module. - -Classes: --------- -- QiskitSimulator: Interfaces with Qiskit simulators for running quantum circuit simulations. - - -""" import logging from typing import Optional @@ -28,36 +7,12 @@ class QiskitSimulator: - """ - A class to interface with Qiskit's Aer simulator and other potential simulators. - - Attributes: - ----------- - _sim : Backend - The backend simulator used for running quantum simulations. - - Methods: - -------- - __init__(self, provider: Optional[str] = None, fusion: Optional[bool] = False, device: str = "CPU") -> None - Initializes the simulator with the specified provider, fusion option, and device type. - - run(self, simobj) -> np.ndarray - Runs a simulation on the provided simulation object and returns the resulting state vector. - """ def __init__( self, provider: Optional[str] = None, fusion: Optional[bool] = False, device: str = "CPU", ) -> None: - """ - Initialize the QiskitSimulator. - - Args: - provider (Optional[str]): The simulator provider to use (e.g., 'ddsim'). Defaults to None. - fusion (Optional[bool]): Whether to enable fusion optimizations. Defaults to False. - device (str): The device to use for simulation ('CPU' or 'GPU'). Defaults to 'CPU'. - """ if provider: if provider == "ddsim": from mqt import ddsim @@ -71,18 +26,6 @@ def __init__( self._sim.set_options(device=device) def run(self, simobj) -> np.ndarray: - """ - Run a simulation on the provided simulation object. - - Args: - simobj: The simulation object containing the quantum circuit to simulate. - - Returns: - np.ndarray: The resulting state vector from the simulation. - - Raises: - QdaoError: If the simulation fails. - """ res = self._sim.run(simobj.circ).result() if not res.success: raise QdaoError( @@ -93,4 +36,4 @@ def run(self, simobj) -> np.ndarray: except Exception as e: sv = np.zeros(1 << simobj.circ.num_qubits) logging.info(f"No state vector for this sub-circuit: {e}") - return sv + return sv \ No newline at end of file diff --git a/qdao/qiskit/utils.py b/qdao/qiskit/utils.py index a976c93..93020ec 100644 --- a/qdao/qiskit/utils.py +++ b/qdao/qiskit/utils.py @@ -10,23 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Utility Functions for Generating Random Circuits -================================================= - -This module provides utility functions for generating random quantum circuits. - -Modules: --------- -- numpy: Handles numerical operations and arrays. -- qiskit.circuit: Provides the QuantumCircuit, QuantumRegister, ClassicalRegister, and exceptions for quantum circuits. -- qiskit.circuit.library.standard_gates: Contains standard gates for quantum circuits. - -Functions: ----------- -- random_circuit(num_qubits: int, depth: int, max_operands: int = 3, measure: bool = False, conditional: bool = False, reset: bool = False, seed: int = None) -> QuantumCircuit - Generate a random quantum circuit with the specified parameters. -""" +"""Utility functions for generating random circuits.""" import numpy as np from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister, Reset @@ -82,20 +66,20 @@ def random_circuit( circ = random_circuit(2, 2, measure=True) circ.draw(output='mpl') - Args: - num_qubits (int): Number of quantum wires. - depth (int): Layers of operations (i.e. critical path length). - max_operands (int): Maximum operands of each gate (between 1 and 3). Defaults to 3. - measure (bool): If True, measure all qubits at the end. Defaults to False. - conditional (bool): If True, insert middle measurements and conditionals. Defaults to False. - reset (bool): If True, insert middle resets. Defaults to False. - seed (int): Sets random seed (optional). Defaults to None. + Args: + num_qubits (int): number of quantum wires + depth (int): layers of operations (i.e. critical path length) + max_operands (int): maximum operands of each gate (between 1 and 3) + measure (bool): if True, measure all qubits at the end + conditional (bool): if True, insert middle measurements and conditionals + reset (bool): if True, insert middle resets + seed (int): sets random seed (optional) Returns: - QuantumCircuit: Constructed random quantum circuit. + QuantumCircuit: constructed circuit Raises: - CircuitError: When invalid options are given. + CircuitError: when invalid options given """ if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") @@ -174,4 +158,4 @@ def random_circuit( if measure: qc.measure(qr, cr) - return qc + return qc \ No newline at end of file diff --git a/qdao/simulator.py b/qdao/simulator.py index 851312b..be3bca0 100644 --- a/qdao/simulator.py +++ b/qdao/simulator.py @@ -35,10 +35,6 @@ class QdaoSimObj: This class encapsulates the quantum circuit and any additional options required to run the simulation. - Attributes: - _objs (tuple): Tuple containing the simulation objects. - _circ (Any): The quantum circuit object. - _run_options (dict): Options for running the simulation. """ def __init__(self, *objs, **options) -> None: @@ -82,8 +78,6 @@ class SimulatorProvider: Methods: get_simulator: Returns an instance of the specified simulator backend. - Attributes: - SIMS (dict): Dictionary mapping backend names to simulator classes. """ @classmethod