diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 99264a7b..320c27fb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,10 +24,13 @@ jobs: - name: Update pip run: pip install --upgrade pip - name: Install black and pylint - run: pip install black pylint + run: pip install black pylint ruff - name: Check files are formatted with black run: | black --check . + - name: Run ruff + run: | + ruff check . - name: Run pylint run: | pylint --recursive=y --ignore=ttn_tutorial.py,mps_tutorial.py */ diff --git a/pytket/extensions/cutensornet/__init__.py b/pytket/extensions/cutensornet/__init__.py index 2215f715..17c01007 100644 --- a/pytket/extensions/cutensornet/__init__.py +++ b/pytket/extensions/cutensornet/__init__.py @@ -13,8 +13,7 @@ # limitations under the License. """Module for conversion from tket primitives to cuQuantum primitives.""" -from .backends import CuTensorNetStateBackend, CuTensorNetShotsBackend -from .general import CuTensorNetHandle - # _metadata.py is copied to the folder after installation. -from ._metadata import __extension_version__, __extension_name__ # type: ignore +from ._metadata import __extension_name__, __extension_version__ # type: ignore +from .backends import CuTensorNetShotsBackend, CuTensorNetStateBackend +from .general import CuTensorNetHandle diff --git a/pytket/extensions/cutensornet/backends/__init__.py b/pytket/extensions/cutensornet/backends/__init__.py index a528be07..1b9bd19b 100644 --- a/pytket/extensions/cutensornet/backends/__init__.py +++ b/pytket/extensions/cutensornet/backends/__init__.py @@ -13,4 +13,4 @@ # limitations under the License. """Backend for utilising the cuQuantum simulator directly from pytket""" -from .cutensornet_backend import CuTensorNetStateBackend, CuTensorNetShotsBackend +from .cutensornet_backend import CuTensorNetShotsBackend, CuTensorNetStateBackend diff --git a/pytket/extensions/cutensornet/backends/cutensornet_backend.py b/pytket/extensions/cutensornet/backends/cutensornet_backend.py index b09d33c0..9afc61df 100644 --- a/pytket/extensions/cutensornet/backends/cutensornet_backend.py +++ b/pytket/extensions/cutensornet/backends/cutensornet_backend.py @@ -14,44 +14,45 @@ """Methods to allow tket circuits to be run on the cuTensorNet simulator.""" -from abc import abstractmethod import warnings - -from typing import List, Union, Optional, Sequence +from abc import abstractmethod +from collections.abc import Sequence +from typing import Optional, Union from uuid import uuid4 -from pytket.circuit import Circuit, OpType -from pytket.backends import ResultHandle, CircuitStatus, StatusEnum, CircuitNotRunError -from pytket.backends.backend import KwargTypes, Backend, BackendResult + +from pytket.backends import CircuitNotRunError, CircuitStatus, ResultHandle, StatusEnum +from pytket.backends.backend import Backend, BackendResult, KwargTypes from pytket.backends.backendinfo import BackendInfo from pytket.backends.resulthandle import _ResultIdTuple +from pytket.circuit import Circuit, OpType from pytket.extensions.cutensornet.general import CuTensorNetHandle from pytket.extensions.cutensornet.general_state import ( GeneralState, ) -from pytket.predicates import ( # type: ignore - Predicate, - NoSymbolsPredicate, - NoClassicalControlPredicate, - NoMidMeasurePredicate, - NoBarriersPredicate, - UserDefinedPredicate, -) from pytket.passes import ( # type: ignore BasePass, - SequencePass, + CustomPass, DecomposeBoxes, + FullPeepholeOptimise, RemoveRedundancies, + SequencePass, SynthesiseTket, - FullPeepholeOptimise, - CustomPass, +) +from pytket.predicates import ( # type: ignore + NoBarriersPredicate, + NoClassicalControlPredicate, + NoMidMeasurePredicate, + NoSymbolsPredicate, + Predicate, + UserDefinedPredicate, ) try: - from cuquantum.cutensornet import StateAttribute, SamplerAttribute # type: ignore + from cuquantum.cutensornet import SamplerAttribute, StateAttribute # type: ignore except ImportError: warnings.warn("local settings failed to import cuquantum", ImportWarning) -from .._metadata import __extension_version__, __extension_name__ +from .._metadata import __extension_name__, __extension_version__ class _CuTensorNetBaseBackend(Backend): @@ -68,7 +69,7 @@ def _result_id_type(self) -> _ResultIdTuple: return (str,) @property - def required_predicates(self) -> List[Predicate]: + def required_predicates(self) -> list[Predicate]: """Returns the minimum set of predicates that a circuit must satisfy. Predicates need to be satisfied before the circuit can be successfully run on @@ -144,7 +145,7 @@ def process_circuits( n_shots: Optional[Union[int, Sequence[int]]] = None, valid_check: bool = True, **kwargs: KwargTypes, - ) -> List[ResultHandle]: + ) -> list[ResultHandle]: """Submits circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the @@ -196,7 +197,7 @@ def process_circuits( n_shots: Optional[Union[int, Sequence[int]]] = None, valid_check: bool = True, **kwargs: KwargTypes, - ) -> List[ResultHandle]: + ) -> list[ResultHandle]: """Submits circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the @@ -273,7 +274,7 @@ def process_circuits( n_shots: Optional[Union[int, Sequence[int]]] = None, valid_check: bool = True, **kwargs: KwargTypes, - ) -> List[ResultHandle]: + ) -> list[ResultHandle]: """Submits circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the @@ -312,10 +313,8 @@ def process_circuits( raise ValueError( "You must specify n_shots when using CuTensorNetShotsBackend." ) - if type(n_shots) == int: - all_shots = [n_shots] * len(circuits) - else: - all_shots = n_shots # type: ignore + + all_shots = [n_shots] * len(circuits) if type(n_shots) is int else n_shots circuit_list = list(circuits) if valid_check: @@ -339,5 +338,5 @@ def _check_all_unitary_or_measurements(circuit: Circuit) -> bool: if cmd.op.type != OpType.Measure: cmd.op.get_unitary() return True - except: + except: # noqa: E722 return False diff --git a/pytket/extensions/cutensornet/general.py b/pytket/extensions/cutensornet/general.py index 9b095664..115f6562 100644 --- a/pytket/extensions/cutensornet/general.py +++ b/pytket/extensions/cutensornet/general.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore -import warnings + import logging +import warnings from logging import Logger - -from typing import Any, Optional +from typing import Any try: import cupy as cp # type: ignore @@ -42,7 +42,7 @@ class CuTensorNetHandle: If not provided, defaults to ``cp.cuda.Device()``. """ - def __init__(self, device_id: Optional[int] = None): + def __init__(self, device_id: int | None = None): self._is_destroyed = False # Make sure CuPy uses the specified device diff --git a/pytket/extensions/cutensornet/general_state/__init__.py b/pytket/extensions/cutensornet/general_state/__init__.py index 04c3357b..ae9930a7 100644 --- a/pytket/extensions/cutensornet/general_state/__init__.py +++ b/pytket/extensions/cutensornet/general_state/__init__.py @@ -13,16 +13,14 @@ # limitations under the License. """Module for simulating circuits with no predetermined tensor network structure.""" -from .utils import circuit_statevector_postselect - from .tensor_network_convert import ( - TensorNetwork, - PauliOperatorTensorNetwork, ExpectationValueTensorNetwork, - tk_to_tensor_network, - measure_qubits_state, - get_operator_expectation_value, + PauliOperatorTensorNetwork, + TensorNetwork, get_circuit_overlap, + get_operator_expectation_value, + measure_qubits_state, + tk_to_tensor_network, ) - from .tensor_network_state import GeneralState +from .utils import circuit_statevector_postselect diff --git a/pytket/extensions/cutensornet/general_state/tensor_network_convert.py b/pytket/extensions/cutensornet/general_state/tensor_network_convert.py index a6c8d3d6..b1dbcee8 100644 --- a/pytket/extensions/cutensornet/general_state/tensor_network_convert.py +++ b/pytket/extensions/cutensornet/general_state/tensor_network_convert.py @@ -21,22 +21,25 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) -from collections import defaultdict import logging +from collections import defaultdict from logging import Logger -from typing import List, Tuple, Union, Any, DefaultDict, Optional +from typing import Any, Optional, Union + import networkx as nx # type: ignore -from networkx.classes.reportviews import OutMultiEdgeView, OutMultiEdgeDataView # type: ignore import numpy as np +from networkx.classes.reportviews import ( # type: ignore + OutMultiEdgeDataView, + OutMultiEdgeView, +) from numpy.typing import NDArray from sympy import Expr # type: ignore -from pytket.utils import Graph -from pytket.pauli import QubitPauliString from pytket.circuit import Circuit, Qubit -from pytket.utils import permute_rows_cols_in_unitary -from pytket.utils.operators import QubitPauliOperator from pytket.extensions.cutensornet.general import set_logger +from pytket.pauli import QubitPauliString +from pytket.utils import Graph, permute_rows_cols_in_unitary +from pytket.utils.operators import QubitPauliOperator class TensorNetwork: @@ -85,7 +88,7 @@ def cuquantum_interleaved(self) -> list: """Returns an interleaved format of the circuit tensor network.""" return self._cuquantum_interleaved - def _get_gate_tensors(self, adj: bool = False) -> DefaultDict[Any, List[Any]]: + def _get_gate_tensors(self, adj: bool = False) -> defaultdict[Any, list[Any]]: """Computes and stores tensors for each gate type from the circuit. The unitaries are reshaped into tensors of bond dimension two prior to being @@ -156,7 +159,8 @@ def _get_gate_tensors(self, adj: bool = False) -> DefaultDict[Any, List[Any]]: .reshape([2] * (2 * com.op.n_qubits)) ) self._logger.debug( - f"Adding unitary: \n {permute_rows_cols_in_unitary(com.op.get_unitary(), com_qix_permut).T.conjugate()}" # type: ignore + f"Adding unitary:\ +\n {permute_rows_cols_in_unitary(com.op.get_unitary(), com_qix_permut).T.conjugate()}" # type: ignore ) else: gate_tensors[i].append( @@ -165,13 +169,14 @@ def _get_gate_tensors(self, adj: bool = False) -> DefaultDict[Any, List[Any]]: ).reshape([2] * (2 * com.op.n_qubits)) ) self._logger.debug( # type: ignore - f"Adding unitary: \n {permute_rows_cols_in_unitary(com.op.get_unitary(),com_qix_permut)}" # type: ignore + f"Adding unitary:\ +\n {permute_rows_cols_in_unitary(com.op.get_unitary(),com_qix_permut)}" # type: ignore ) break self._logger.debug(f"Gate tensors: \n{gate_tensors}\n") return gate_tensors - def _assign_node_tensors(self, adj: bool = False) -> List[Any]: + def _assign_node_tensors(self, adj: bool = False) -> list[Any]: """Creates a list of tensors representing circuit gates (tensor network nodes). Args: @@ -208,17 +213,18 @@ def _assign_node_tensors(self, adj: bool = False) -> List[Any]: ) self._logger.debug( f"criteria: " - f"{(src_ports[0] < src_ports[1]) != (unit_idx[0] < unit_idx[1])}" # pylint: disable=line-too-long + f"{(src_ports[0] < src_ports[1]) != + (unit_idx[0] < unit_idx[1])}" # pylint: disable=line-too-long ) if (src_ports[0] < src_ports[1]) != (unit_idx[0] < unit_idx[1]): node_tensors.append(self._gate_tensors[node[1]["desc"]][1]) - self._logger.debug(f"Adding an upward gate tensor") + self._logger.debug("Adding an upward gate tensor") else: node_tensors.append(self._gate_tensors[node[1]["desc"]][0]) - self._logger.debug(f"Adding a downward gate tensor") + self._logger.debug("Adding a downward gate tensor") else: node_tensors.append(self._gate_tensors[node[1]["desc"]][0]) - self._logger.debug(f"Adding a 1-qubit gate tensor") + self._logger.debug("Adding a 1-qubit gate tensor") else: if node[1]["desc"] == "Output": self._output_nodes.append(i) @@ -233,7 +239,7 @@ def _assign_node_tensors(self, adj: bool = False) -> List[Any]: def _get_tn_indices( self, net: nx.MultiDiGraph, adj: bool = False - ) -> Tuple[List[Any], dict[Qubit, int]]: + ) -> tuple[list[Any], dict[Qubit, int]]: """Computes indices of the edges of the tensor network nodes (tensors). Indices are computed such that they range from high (for circuit leftmost gates) @@ -280,7 +286,7 @@ def _get_tn_indices( ] eids_sorted = sorted(eids, key=abs) qnames_graph_ordered = [qname for qname in self._graph.output_names.values()] - oids_graph_ordered = [oid for oid in self._graph.output_names.keys()] + oids_graph_ordered = [oid for oid in self._graph.output_names] eids_qubit_ordered = [ eids_sorted[qnames_graph_ordered.index(q)] for q in self._qubit_names_ilo ] # Order eid's in the same way as qnames_graph_ordered as compared to ILO @@ -360,7 +366,7 @@ def _get_tn_indices( @staticmethod def _order_edges_for_multiqubit_gate( - edge_indices: DefaultDict[Any, List[Tuple[Any, int]]], + edge_indices: defaultdict[Any, list[tuple[Any, int]]], edges: OutMultiEdgeView, edges_data: OutMultiEdgeDataView, offset: int, @@ -574,7 +580,7 @@ def __init__( self._logger = set_logger("PauliOperatorTensorNetwork", loglevel) self._pauli_tensors = [self.PAULI[pauli.name] for pauli in paulis.map.values()] self._logger.debug(f"Pauli tensors: {self._pauli_tensors}") - qubits = [q for q in paulis.map.keys()] + qubits = [q for q in paulis.map] # qubit_names = [ # "".join([q.reg_name, "".join([f"[{str(i)}]" for i in q.index])]) # for q in paulis.map.keys() @@ -652,7 +658,7 @@ def _make_interleaved(self) -> list: return tn_concatenated -def tk_to_tensor_network(tkc: Circuit) -> List[Union[NDArray, List]]: +def tk_to_tensor_network(tkc: Circuit) -> list[Union[NDArray, list]]: """Converts pytket circuit into a tensor network. Args: diff --git a/pytket/extensions/cutensornet/general_state/tensor_network_state.py b/pytket/extensions/cutensornet/general_state/tensor_network_state.py index bedfc0f2..aa26bb2c 100644 --- a/pytket/extensions/cutensornet/general_state/tensor_network_state.py +++ b/pytket/extensions/cutensornet/general_state/tensor_network_state.py @@ -13,9 +13,10 @@ # limitations under the License. from __future__ import annotations + import logging -from typing import Union, Optional, Tuple, Dict import warnings +from typing import TYPE_CHECKING try: import cupy as cp # type: ignore @@ -23,12 +24,16 @@ warnings.warn("local settings failed to import cupy", ImportWarning) import numpy as np from sympy import Expr # type: ignore -from numpy.typing import NDArray -from pytket.circuit import Circuit, Qubit, Bit, OpType + +from pytket.backends.backendresult import BackendResult +from pytket.circuit import Bit, Circuit, OpType, Qubit from pytket.extensions.cutensornet.general import CuTensorNetHandle, set_logger from pytket.utils import OutcomeArray -from pytket.utils.operators import QubitPauliOperator -from pytket.backends.backendresult import BackendResult + +if TYPE_CHECKING: + from numpy.typing import NDArray + + from pytket.utils.operators import QubitPauliOperator try: import cuquantum as cq # type: ignore @@ -91,7 +96,7 @@ def __init__( for com in self._circuit.get_commands(): try: gate_unitary = com.op.get_unitary() - except: + except: # noqa: E722 raise ValueError( "All commands in the circuit must be unitary gates. The circuit " f"contains {com}; no unitary matrix could be retrived for it." @@ -113,10 +118,10 @@ def __init__( def get_statevector( self, - attributes: Optional[dict] = None, + attributes: dict | None = None, scratch_fraction: float = 0.75, on_host: bool = True, - ) -> Union[cp.ndarray, np.ndarray]: + ) -> cp.ndarray | np.ndarray: """Contracts the circuit and returns the final statevector. Args: @@ -228,7 +233,7 @@ def get_statevector( def expectation_value( self, operator: QubitPauliOperator, - attributes: Optional[dict] = None, + attributes: dict | None = None, scratch_fraction: float = 0.75, ) -> complex: """Calculates the expectation value of the given operator. @@ -273,7 +278,7 @@ def expectation_value( self._logger.debug(f" {numeric_coeff}, {pauli_string}") # Raise an error if the operator acts on qubits that are not in the circuit - if any(q not in self._circuit.qubits for q in pauli_string.map.keys()): + if any(q not in self._circuit.qubits for q in pauli_string.map): raise ValueError( f"The operator is acting on qubits {pauli_string.map.keys()}, " "but some of these are not present in the circuit, whose set of " @@ -287,9 +292,7 @@ def expectation_value( num_pauli = len(qubit_pauli_map) num_modes = (1,) * num_pauli - state_modes = tuple( - (self._qubit_idx_map[qb],) for qb in qubit_pauli_map.keys() - ) + state_modes = tuple((self._qubit_idx_map[qb],) for qb in qubit_pauli_map) gate_data = tuple(tensor.data.ptr for tensor in qubit_pauli_map.values()) cutn.network_operator_append_product( @@ -406,7 +409,7 @@ def expectation_value( def sample( self, n_shots: int, - attributes: Optional[dict] = None, + attributes: dict | None = None, scratch_fraction: float = 0.75, ) -> BackendResult: """Obtains samples from the measurements at the end of the circuit. @@ -566,7 +569,7 @@ def _formatted_tensor(matrix: NDArray, n_qubits: int) -> cp.ndarray: return cupy_matrix.reshape([2] * (2 * n_qubits), order="F") -def _remove_meas_and_implicit_swaps(circ: Circuit) -> Tuple[Circuit, Dict[Qubit, Bit]]: +def _remove_meas_and_implicit_swaps(circ: Circuit) -> tuple[Circuit, dict[Qubit, Bit]]: """Convert a pytket Circuit to an equivalent circuit with no measurements or implicit swaps. The measurements are returned as a map between qubits and bits. diff --git a/pytket/extensions/cutensornet/general_state/utils.py b/pytket/extensions/cutensornet/general_state/utils.py index d0b77927..278d30c8 100644 --- a/pytket/extensions/cutensornet/general_state/utils.py +++ b/pytket/extensions/cutensornet/general_state/utils.py @@ -13,8 +13,9 @@ # limitations under the License. from numpy.typing import NDArray + from pytket.backends.backendresult import BackendResult -from pytket.circuit import Qubit, Circuit +from pytket.circuit import Circuit, Qubit def _reorder_qlist( diff --git a/pytket/extensions/cutensornet/structured_state/__init__.py b/pytket/extensions/cutensornet/structured_state/__init__.py index 3daffd99..6faec933 100644 --- a/pytket/extensions/cutensornet/structured_state/__init__.py +++ b/pytket/extensions/cutensornet/structured_state/__init__.py @@ -22,11 +22,9 @@ from pytket.extensions.cutensornet import CuTensorNetHandle from .general import Config, StructuredState -from .simulation import SimulationAlgorithm, simulate, prepare_circuit_mps - -from .mps import DirMPS, MPS +from .mps import MPS, DirMPS from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO - +from .simulation import SimulationAlgorithm, prepare_circuit_mps, simulate from .ttn import TTN, DirTTN from .ttn_gate import TTNxGate diff --git a/pytket/extensions/cutensornet/structured_state/classical.py b/pytket/extensions/cutensornet/structured_state/classical.py index d49ac4d9..3190c34a 100644 --- a/pytket/extensions/cutensornet/structured_state/classical.py +++ b/pytket/extensions/cutensornet/structured_state/classical.py @@ -12,23 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union, Any +from typing import Any, Union from pytket.circuit import ( - Op, - OpType, Bit, BitRegister, - SetBitsOp, - CopyBitsOp, - RangePredicateOp, + BitWiseOp, ClassicalExpBox, + CopyBitsOp, LogicExp, - BitWiseOp, + Op, + OpType, + RangePredicateOp, RegWiseOp, + SetBitsOp, ) - ExtendedLogicExp = Union[LogicExp, Bit, BitRegister, int] diff --git a/pytket/extensions/cutensornet/structured_state/general.py b/pytket/extensions/cutensornet/structured_state/general.py index 0cc56230..4ebe3d40 100644 --- a/pytket/extensions/cutensornet/structured_state/general.py +++ b/pytket/extensions/cutensornet/structured_state/general.py @@ -12,31 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore -from abc import ABC, abstractmethod -import warnings + import logging -from typing import Any, Optional, Type +import warnings +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any import numpy as np # type: ignore from pytket.circuit import ( + Bit, Command, + Conditional, Op, OpType, Qubit, - Bit, - Conditional, ) -from pytket.pauli import QubitPauliString try: import cupy as cp # type: ignore except ImportError: warnings.warn("local settings failed to import cupy", ImportWarning) -from pytket.extensions.cutensornet import CuTensorNetHandle + from .classical import apply_classical_command, from_little_endian +if TYPE_CHECKING: + from pytket.extensions.cutensornet import CuTensorNetHandle + from pytket.pauli import QubitPauliString + # An alias for the CuPy type used for tensors try: Tensor = cp.ndarray @@ -49,10 +53,10 @@ class Config: def __init__( self, - chi: Optional[int] = None, - truncation_fidelity: Optional[float] = None, - seed: Optional[int] = None, - float_precision: Type[Any] = np.float64, + chi: int | None = None, + truncation_fidelity: float | None = None, + seed: int | None = None, + float_precision: type[Any] = np.float64, value_of_zero: float = 1e-16, leaf_size: int = 8, k: int = 4, @@ -225,7 +229,7 @@ def _apply_command( elif op.is_gate(): # Either a unitary gate or a not supported "gate" try: unitary = op.get_unitary() - except: + except: # noqa E722 raise ValueError(f"The command {op.type} introduced is not supported.") # Load the gate's unitary to the GPU memory diff --git a/pytket/extensions/cutensornet/structured_state/mps.py b/pytket/extensions/cutensornet/structured_state/mps.py index b9aee6e4..46b5677d 100644 --- a/pytket/extensions/cutensornet/structured_state/mps.py +++ b/pytket/extensions/cutensornet/structured_state/mps.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore + import warnings -from typing import Union, Optional from enum import Enum - from random import Random # type: ignore + import numpy as np # type: ignore try: @@ -29,10 +29,9 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) -from pytket.circuit import Op, OpType, Qubit, Bit -from pytket.pauli import Pauli, QubitPauliString - +from pytket.circuit import Bit, Op, OpType, Qubit from pytket.extensions.cutensornet.general import CuTensorNetHandle, set_logger +from pytket.pauli import Pauli, QubitPauliString from .general import Config, StructuredState, Tensor @@ -69,7 +68,7 @@ def __init__( libhandle: CuTensorNetHandle, qubits: list[Qubit], config: Config, - bits: Optional[list[Bit]] = None, + bits: list[Bit] | None = None, ): """Initialise an MPS on the computational state ``|0>`` @@ -269,7 +268,7 @@ def add_qubit(self, new_qubit: Qubit, position: int, state: int = 0) -> MPS: options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - if new_qubit in self.qubit_position.keys(): + if new_qubit in self.qubit_position: raise ValueError( f"Qubit {new_qubit} cannot be added, it already is in the MPS." ) @@ -338,7 +337,7 @@ def canonicalise(self, l_pos: int, r_pos: int) -> None: for pos in reversed(range(r_pos + 1, len(self))): self.canonicalise_tensor(pos, form=DirMPS.RIGHT) - self._logger.debug(f"Finished canonicalisation.") + self._logger.debug("Finished canonicalisation.") def canonicalise_tensor(self, pos: int, form: DirMPS) -> None: """Canonicalises a tensor from an MPS object. @@ -406,7 +405,7 @@ def canonicalise_tensor(self, pos: int, form: DirMPS) -> None: Q, R = tensor.decompose( subscripts, T, method=tensor.QRMethod(), options=options ) - self._logger.debug(f"QR decomposition finished.") + self._logger.debug("QR decomposition finished.") # Contract R into Tnext subscripts = R_bonds + "," + Tnext_bonds + "->" + result_bonds @@ -501,7 +500,7 @@ def vdot(self, other: MPS) -> complex: # type: ignore def _get_interleaved_representation( self, conj: bool = False - ) -> list[Union[cp.ndarray, str]]: + ) -> list[cp.ndarray | str]: """Returns the interleaved representation of the MPS used by cuQuantum. Args: @@ -760,7 +759,7 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float: """ self._flush() - for q in pauli_string.map.keys(): + for q in pauli_string.map: if q not in self.qubit_position: raise ValueError(f"Qubit {q} is not a qubit in the MPS.") diff --git a/pytket/extensions/cutensornet/structured_state/mps_gate.py b/pytket/extensions/cutensornet/structured_state/mps_gate.py index 4a7fa7f0..5f9f1586 100644 --- a/pytket/extensions/cutensornet/structured_state/mps_gate.py +++ b/pytket/extensions/cutensornet/structured_state/mps_gate.py @@ -12,8 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore -import warnings + import logging +import warnings try: import cupy as cp # type: ignore @@ -26,9 +27,14 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) -from pytket.circuit import Qubit + +from typing import TYPE_CHECKING + from .mps import MPS, DirMPS +if TYPE_CHECKING: + from pytket.circuit import Qubit + class MPSxGate(MPS): """Implements a gate-by-gate contraction algorithm to calculate the output state @@ -113,10 +119,7 @@ def _apply_2q_unitary(self, unitary: cp.ndarray, q0: Qubit, q1: Qubit) -> MPSxGa # S -> shared bond of the gate tensor's SVD # a,b,c -> the virtual bonds of the tensors - if l_pos == positions[0]: - gate_bonds = "LRlr" - else: # Implicit swap - gate_bonds = "RLrl" + gate_bonds = "LRlr" if l_pos == positions[0] else "RLrl" # Apply SVD on the gate tensor to remove any zero singular values ASAP svd_method = tensor.SVDMethod( @@ -132,7 +135,7 @@ def _apply_2q_unitary(self, unitary: cp.ndarray, q0: Qubit, q1: Qubit) -> MPSxGa # Contract self._logger.debug("Contracting the two-qubit gate with its site tensors...") T = cq.contract( - f"SLl,abl,SRr,bcr->acLR", + "SLl,abl,SRr,bcr->acLR", U, self.tensors[l_pos], V, @@ -249,10 +252,7 @@ def _apply_2q_unitary_nonadjacent( # a,b -> virtual bonds of the MPS # m,M -> virtual bonds connected to the "message tensor" - if l_pos == positions[0]: - gate_bonds = "LRlr" - else: # Implicit swap - gate_bonds = "RLrl" + gate_bonds = "LRlr" if l_pos == positions[0] else "RLrl" # Apply SVD on the gate tensor to remove any zero singular values ASAP svd_method = tensor.SVDMethod( diff --git a/pytket/extensions/cutensornet/structured_state/mps_mpo.py b/pytket/extensions/cutensornet/structured_state/mps_mpo.py index 4dfbd9e5..52cfb275 100644 --- a/pytket/extensions/cutensornet/structured_state/mps_mpo.py +++ b/pytket/extensions/cutensornet/structured_state/mps_mpo.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore -import warnings -from typing import Optional, Union +import warnings +from typing import TYPE_CHECKING import numpy as np # type: ignore @@ -28,15 +28,19 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) -from pytket.circuit import Qubit, Bit -from pytket.extensions.cutensornet import CuTensorNetHandle -from .general import Tensor, Config + from .mps import ( - DirMPS, MPS, + DirMPS, ) from .mps_gate import MPSxGate +if TYPE_CHECKING: + from pytket.circuit import Bit, Qubit + from pytket.extensions.cutensornet import CuTensorNetHandle + + from .general import Config, Tensor + class MPSxMPO(MPS): """Implements a batched--gate contraction algorithm (DMRG-like) to calculate @@ -49,7 +53,7 @@ def __init__( libhandle: CuTensorNetHandle, qubits: list[Qubit], config: Config, - bits: Optional[list[Bit]] = None, + bits: list[Bit] | None = None, ): """Initialise an MPS on the computational state ``|0>``. @@ -236,10 +240,7 @@ def _apply_2q_unitary(self, unitary: cp.ndarray, q0: Qubit, q1: Qubit) -> MPSxMP # s -> virtual bond after QR decomposition # Assign the bond IDs for the gate - if l_pos == positions[0]: - gate_bonds = "LRlr" - else: # Implicit swap - gate_bonds = "RLrl" + gate_bonds = "LRlr" if l_pos == positions[0] else "RLrl" # Apply SVD on the gate tensor to remove any zero singular values ASAP svd_method = tensor.SVDMethod( @@ -429,7 +430,7 @@ def update_sweep_cache(pos: int, direction: DirMPS) -> None: # The MPO tensor at this position interleaved_rep.append(mpo_tensor) - mpo_bonds: list[Union[int, str]] = list(self._bond_ids[pos][i]) + mpo_bonds: list[int | str] = list(self._bond_ids[pos][i]) if i == 0: # The input bond of the first MPO tensor must connect to the # physical bond of the correspondong ``self.tensors`` tensor @@ -446,11 +447,11 @@ def update_sweep_cache(pos: int, direction: DirMPS) -> None: interleaved_rep.append(r_cached_tensors[-1]) r_cached_bonds = self._get_column_bonds(pos + 1, DirMPS.LEFT) interleaved_rep.append(["r", "R"] + r_cached_bonds) - elif direction == DirMPS.RIGHT: - if pos != 0: # Otherwise, there is nothing cached yet - interleaved_rep.append(l_cached_tensors[-1]) - l_cached_bonds = self._get_column_bonds(pos - 1, DirMPS.RIGHT) - interleaved_rep.append(["l", "L"] + l_cached_bonds) + elif direction == DirMPS.RIGHT and pos != 0: + # Otherwise, there is nothing cached yet + interleaved_rep.append(l_cached_tensors[-1]) + l_cached_bonds = self._get_column_bonds(pos - 1, DirMPS.RIGHT) + interleaved_rep.append(["l", "L"] + l_cached_bonds) # Figure out the ID of the bonds of the contracted tensor if direction == DirMPS.LEFT: @@ -478,7 +479,7 @@ def update_sweep_cache(pos: int, direction: DirMPS) -> None: self._logger.debug("Completed update of the sweep cache.") def update_variational_tensor( - pos: int, left_tensor: Optional[Tensor], right_tensor: Optional[Tensor] + pos: int, left_tensor: Tensor | None, right_tensor: Tensor | None ) -> float: """Update the tensor at ``pos`` of the variational MPS using ``left_tensor`` (and ``right_tensor``) which is meant to contain the contraction of all @@ -500,7 +501,7 @@ def update_variational_tensor( # The MPO tensor at this position interleaved_rep.append(mpo_tensor) - mpo_bonds: list[Union[int, str]] = list(self._bond_ids[pos][i]) + mpo_bonds: list[int | str] = list(self._bond_ids[pos][i]) if i == 0: # The input bond of the first MPO tensor must connect to the # physical bond of the correspondong ``self.tensors`` tensor @@ -569,7 +570,7 @@ def update_variational_tensor( # Repeat sweeps until the fidelity converges sweep_direction = DirMPS.RIGHT while not np.isclose(prev_fidelity, sweep_fidelity, atol=self._cfg.optim_delta): - self._logger.info(f"Doing another optimisation sweep...") + self._logger.info("Doing another optimisation sweep...") prev_fidelity = sweep_fidelity if sweep_direction == DirMPS.RIGHT: diff --git a/pytket/extensions/cutensornet/structured_state/simulation.py b/pytket/extensions/cutensornet/structured_state/simulation.py index e4756085..db5bf643 100644 --- a/pytket/extensions/cutensornet/structured_state/simulation.py +++ b/pytket/extensions/cutensornet/structured_state/simulation.py @@ -11,28 +11,27 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Any import warnings +from collections import defaultdict # type: ignore from enum import Enum - from pathlib import Path -from collections import defaultdict # type: ignore -import numpy as np # type: ignore +from typing import Any, Optional import networkx as nx # type: ignore +import numpy as np # type: ignore try: import kahypar # type: ignore except ImportError: warnings.warn("local settings failed to import kahypar", ImportWarning) -from pytket.circuit import Circuit, Command, OpType, Qubit -from pytket.transform import Transform from pytket.architecture import Architecture +from pytket.circuit import Circuit, Command, OpType, Qubit +from pytket.extensions.cutensornet.general import CuTensorNetHandle, set_logger from pytket.passes import DefaultMappingPass from pytket.predicates import CompilationUnit +from pytket.transform import Transform -from pytket.extensions.cutensornet.general import CuTensorNetHandle, set_logger from .general import Config, StructuredState from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO diff --git a/pytket/extensions/cutensornet/structured_state/ttn.py b/pytket/extensions/cutensornet/structured_state/ttn.py index 3a1a412e..0a114bb9 100644 --- a/pytket/extensions/cutensornet/structured_state/ttn.py +++ b/pytket/extensions/cutensornet/structured_state/ttn.py @@ -12,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore + +import math # type: ignore import warnings -from typing import Optional, Union from enum import IntEnum - from random import Random # type: ignore -import math # type: ignore -import numpy as np # type: ignore +from typing import TYPE_CHECKING try: import cupy as cp # type: ignore @@ -30,13 +29,16 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) -from pytket.circuit import Qubit, Bit -from pytket.pauli import QubitPauliString - +from pytket.circuit import Bit, Qubit from pytket.extensions.cutensornet.general import CuTensorNetHandle, set_logger from .general import Config, StructuredState, Tensor +if TYPE_CHECKING: + import numpy as np + + from pytket.pauli import QubitPauliString + class DirTTN(IntEnum): """An enum to refer to relative directions within the TTN.""" @@ -66,7 +68,7 @@ class TreeNode: def __init__(self, tensor: Tensor, is_leaf: bool = False): self.tensor = tensor self.is_leaf = is_leaf - self.canonical_form: Optional[DirTTN] = None + self.canonical_form: DirTTN | None = None def copy(self) -> TreeNode: new_node = TreeNode( @@ -96,7 +98,7 @@ def __init__( libhandle: CuTensorNetHandle, qubit_partition: dict[int, list[Qubit]], config: Config, - bits: Optional[list[Bit]] = None, + bits: list[Bit] | None = None, ): """Initialise a TTN on the computational state ``|0>``. @@ -159,12 +161,12 @@ def __init__( # Calculate the root path of this group path = [] - for l in reversed(range(n_levels)): - if k < 2**l: + for le in reversed(range(n_levels)): + if k < 2**le: path.append(DirTTN.LEFT) else: path.append(DirTTN.RIGHT) - k -= 2**l + k -= 2**le # Add each qubit to the qubit_position dictionary for i, q in enumerate(qubits): @@ -215,7 +217,7 @@ def is_valid(self) -> bool: """ chi_ok = all( self.get_dimension(path, DirTTN.PARENT) <= self._cfg.chi - for path in self.nodes.keys() + for path in self.nodes ) phys_ok = all( self.nodes[path].tensor.shape[bond] == 2 @@ -227,7 +229,7 @@ def is_valid(self) -> bool: shape_ok = all( self.get_dimension(path, DirTTN.PARENT) == self.get_dimension(path[:-1], path[-1]) - for path in self.nodes.keys() + for path in self.nodes if len(path) != 0 ) shape_ok = shape_ok and self.get_dimension((), DirTTN.PARENT) == 1 @@ -332,9 +334,7 @@ def apply_qubit_relabelling(self, qubit_map: dict[Qubit, Qubit]) -> TTN: self._logger.debug(f"Relabelled qubits... {qubit_map}") return self - def canonicalise( - self, center: Union[RootPath, Qubit], unsafe: bool = False - ) -> Tensor: + def canonicalise(self, center: RootPath | Qubit, unsafe: bool = False) -> Tensor: """Canonicalise the TTN so that all tensors are isometries from ``center``. Args: @@ -368,10 +368,10 @@ def canonicalise( # Separate nodes to be canonicalised towards children from those towards parent towards_child = [] towards_parent = [] - for path in self.nodes.keys(): + for path in self.nodes: # Nodes towards children are closer to the root and coincide in the path if len(path) < len(target_path) and all( - path[l] == target_path[l] for l in range(len(path)) + path[le] == target_path[le] for le in range(len(path)) ): towards_child.append(path) # If the center is a physical bond (qubit), its node is skipped @@ -759,9 +759,7 @@ def get_qubits(self) -> set[Qubit]: """Returns the set of qubits that this TTN is defined on.""" return set(self.qubit_position.keys()) - def get_interleaved_representation( - self, conj: bool = False - ) -> list[Union[Tensor, str]]: + def get_interleaved_representation(self, conj: bool = False) -> list[Tensor | str]: """Returns the interleaved representation of the TTN used by cuQuantum. Args: diff --git a/pytket/extensions/cutensornet/structured_state/ttn_gate.py b/pytket/extensions/cutensornet/structured_state/ttn_gate.py index 27e8531e..2e5452bb 100644 --- a/pytket/extensions/cutensornet/structured_state/ttn_gate.py +++ b/pytket/extensions/cutensornet/structured_state/ttn_gate.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore + import warnings try: @@ -25,9 +26,14 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) -from pytket.circuit import Qubit + +from typing import TYPE_CHECKING + from .ttn import TTN, DirTTN, RootPath +if TYPE_CHECKING: + from pytket.circuit import Qubit + class TTNxGate(TTN): """Implements a gate-by-gate contraction algorithm to calculate the output state @@ -641,10 +647,7 @@ def _contract_decomp_bond_tensor_into_ttn( # Contract V to the parent node of the bond direction = bond_address[-1] - if direction == DirTTN.LEFT: - indices = "lrp,sl->srp" - else: - indices = "lrp,sr->lsp" + indices = "lrp,sl->srp" if direction == DirTTN.LEFT else "lrp,sr->lsp" self.nodes[bond_address[:-1]].tensor = cq.contract( indices, self.nodes[bond_address[:-1]].tensor, diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..f2146f67 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,44 @@ +target-version = "py39" + +line-length = 88 + +extend-exclude = ["examples"] + +select = [ + "E", # pycodestyle Errors + "W", # pycodestyle Warnings + + # "A", # flake8-builtins + # "B", # flake8-Bugbear + # "C4", # flake8-comprehensions + # "COM", # flake8-commas + # "EXE", # flake8-executable + "F", # pyFlakes + # "FA", # flake8-future-annotations + # "FIX", # flake8-fixme + # "FLY", # flynt + "I", # isort + # "INP", # flake8-no-pep420 + # "ISC", # flake8-implicit-str-concat + # "N", # pep8-Naming + # "NPY", # NumPy-specific + # "PERF", # Perflint + # "PGH", # pygrep-hooks + # "PIE", # flake8-pie + # "PL", # pylint + # "PT", # flake8-pytest-style + # "RSE", # flake8-raise + # "RUF", # Ruff-specific + # "S", # flake8-bandit (Security) + "SIM", # flake8-simplify + # "SLF", # flake8-self + "T20", # flake8-print + "TCH", # flake8-type-checking + # "TRY", # tryceratops + "UP", # pyupgrade + # "YTT", # flake8-2020 +] + +[per-file-ignores] +".github/workflows/docs/conf.py" = ["E402"] +"__init__.py" = ["F401"] # module imported but unused (6) diff --git a/setup.py b/setup.py index 23e033b2..bf8fda20 100644 --- a/setup.py +++ b/setup.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import shutil import os -from setuptools import setup, find_namespace_packages # type: ignore +import shutil +from pathlib import Path + +from setuptools import find_namespace_packages, setup # type: ignore metadata: dict = {} with open("_metadata.py") as fp: @@ -37,7 +39,7 @@ "Tracker": "https://github.com/CQCL/pytket-cutensornet/issues", }, description="Extension for pytket, providing access to the cuTensorNet Python API.", - long_description=open("README.md").read(), + long_description=(Path(__file__).parent / "README.md").read_text(), long_description_content_type="text/markdown", license="Apache 2", packages=find_namespace_packages(include=["pytket.*"]), diff --git a/tests/conftest.py b/tests/conftest.py index 5297f495..1adcc45c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,9 @@ -import pytest import numpy as np +import pytest from scipy.stats import unitary_group # type: ignore -from pytket.circuit import Circuit, OpType, Unitary2qBox, ToffoliBox, Qubit -from pytket.passes import DecomposeBoxes, CnXPairwiseDecomposition + +from pytket.circuit import Circuit, OpType, Qubit, ToffoliBox, Unitary2qBox +from pytket.passes import CnXPairwiseDecomposition, DecomposeBoxes from pytket.transform import Transform diff --git a/tests/test_cutensornet_backend.py b/tests/test_cutensornet_backend.py index 25c9d31c..b3549518 100644 --- a/tests/test_cutensornet_backend.py +++ b/tests/test_cutensornet_backend.py @@ -1,11 +1,18 @@ import numpy as np import pytest -from pytket.circuit import Circuit, BasisOrder, ToffoliBox, DummyBox, OpType # type: ignore -from pytket.passes import CliffordSimp # type: ignore + +from pytket.circuit import ( # type: ignore + BasisOrder, + Circuit, + DummyBox, + OpType, + ToffoliBox, +) from pytket.extensions.cutensornet.backends import ( - CuTensorNetStateBackend, CuTensorNetShotsBackend, + CuTensorNetStateBackend, ) +from pytket.passes import CliffordSimp # type: ignore def test_bell() -> None: diff --git a/tests/test_cutensornet_postselect.py b/tests/test_cutensornet_postselect.py index a7c04771..1a4e8a66 100644 --- a/tests/test_cutensornet_postselect.py +++ b/tests/test_cutensornet_postselect.py @@ -1,18 +1,19 @@ -import numpy as np import cuquantum as cq # type: ignore +import numpy as np import pytest -from pytket.circuit import Qubit, Circuit # type: ignore -from pytket.pauli import Pauli, QubitPauliString # type: ignore -from pytket.utils import QubitPauliOperator + +from pytket.circuit import Circuit, Qubit # type: ignore from pytket.extensions.cutensornet.backends import CuTensorNetStateBackend from pytket.extensions.cutensornet.general_state.tensor_network_convert import ( # type: ignore TensorNetwork, - measure_qubits_state, get_operator_expectation_value, + measure_qubits_state, ) from pytket.extensions.cutensornet.general_state.utils import ( circuit_statevector_postselect, ) +from pytket.pauli import Pauli, QubitPauliString # type: ignore +from pytket.utils import QubitPauliOperator @pytest.mark.parametrize( diff --git a/tests/test_general_state.py b/tests/test_general_state.py index f3aadf5b..a0b7deaf 100644 --- a/tests/test_general_state.py +++ b/tests/test_general_state.py @@ -1,13 +1,15 @@ import random + import numpy as np import pytest -from pytket.circuit import Circuit, ToffoliBox, Qubit, Bit -from pytket.passes import DecomposeBoxes, CnXPairwiseDecomposition -from pytket.transform import Transform -from pytket.pauli import QubitPauliString, Pauli -from pytket.utils.operators import QubitPauliOperator + +from pytket.circuit import Bit, Circuit, Qubit, ToffoliBox from pytket.extensions.cutensornet.general_state import GeneralState from pytket.extensions.cutensornet.structured_state import CuTensorNetHandle +from pytket.passes import CnXPairwiseDecomposition, DecomposeBoxes +from pytket.pauli import Pauli, QubitPauliString +from pytket.transform import Transform +from pytket.utils.operators import QubitPauliOperator @pytest.mark.parametrize( @@ -242,10 +244,7 @@ def test_sampler(circuit: Circuit, measure_all: bool) -> None: sv_pytket = circuit.get_statevector() # Add measurements to qubits - if measure_all: - num_measured = circuit.n_qubits - else: - num_measured = circuit.n_qubits // 2 + num_measured = circuit.n_qubits if measure_all else circuit.n_qubits // 2 for i, q in enumerate(circuit.qubits): if i < num_measured: # Skip the least significant qubits diff --git a/tests/test_structured_state.py b/tests/test_structured_state.py index d3d78d61..aeaba273 100644 --- a/tests/test_structured_state.py +++ b/tests/test_structured_state.py @@ -1,29 +1,29 @@ -from typing import Any, Union import random # type: ignore -import pytest +from typing import Any, Union -import cuquantum as cq # type: ignore import cupy as cp # type: ignore +import cuquantum as cq # type: ignore import numpy as np # type: ignore +import pytest -from pytket.circuit import Circuit, Qubit, OpType # type: ignore -from pytket.pauli import Pauli, QubitPauliString # type: ignore +from pytket.circuit import Circuit, OpType, Qubit # type: ignore +from pytket.extensions.cutensornet.general_state.utils import ( + circuit_statevector_postselect, +) from pytket.extensions.cutensornet.structured_state import ( - CuTensorNetHandle, - Config, MPS, + Config, + CuTensorNetHandle, + DirTTN, MPSxGate, MPSxMPO, + SimulationAlgorithm, TTNxGate, - DirTTN, - simulate, prepare_circuit_mps, - SimulationAlgorithm, + simulate, ) from pytket.extensions.cutensornet.structured_state.ttn import RootPath -from pytket.extensions.cutensornet.general_state.utils import ( - circuit_statevector_postselect, -) +from pytket.pauli import Pauli, QubitPauliString # type: ignore def test_libhandle_manager() -> None: diff --git a/tests/test_structured_state_conditionals.py b/tests/test_structured_state_conditionals.py index c5b6a116..8e813d99 100644 --- a/tests/test_structured_state_conditionals.py +++ b/tests/test_structured_state_conditionals.py @@ -1,26 +1,23 @@ -import pytest - import numpy as np +import pytest from pytket.circuit import ( - Circuit, + Bit, CircBox, + Circuit, OpType, Qubit, - Bit, if_not_bit, reg_eq, ) from pytket.circuit.logic_exp import BitWiseOp, create_bit_logic_exp - from pytket.extensions.cutensornet.structured_state import ( - CuTensorNetHandle, Config, - simulate, + CuTensorNetHandle, SimulationAlgorithm, + simulate, ) - # This first suite of tests comes from the pytket-qir extension # (see https://github.com/CQCL/pytket-qir/blob/main/tests/conditional_test.py) # Further down, there are tests to check that the simulation works correctly. @@ -336,7 +333,7 @@ def test_correctness_copy_bits() -> None: cfg = Config() state = simulate(libhandle, circ, SimulationAlgorithm.MPSxGate, cfg) # Check that the copied register has the correct values - assert state.get_bits()[copied[0]] == False and state.get_bits()[copied[1]] == True + assert state.get_bits()[copied[0]] is False and state.get_bits()[copied[1]] is True def test_correctness_teleportation_bit() -> None: diff --git a/tests/test_tensor_network_convert.py b/tests/test_tensor_network_convert.py index c8816712..8c246bfb 100644 --- a/tests/test_tensor_network_convert.py +++ b/tests/test_tensor_network_convert.py @@ -1,12 +1,14 @@ -from typing import List, Union -import warnings -import random import cmath +import random +import warnings +from typing import Union + import numpy as np -from numpy.typing import NDArray import pytest -from pytket.circuit import ToffoliBox, Qubit # type: ignore -from pytket.passes import DecomposeBoxes, CnXPairwiseDecomposition # type: ignore +from numpy.typing import NDArray + +from pytket.circuit import Qubit, ToffoliBox # type: ignore +from pytket.passes import CnXPairwiseDecomposition, DecomposeBoxes # type: ignore from pytket.transform import Transform # type: ignore try: @@ -14,18 +16,17 @@ except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Circuit - from pytket.extensions.cutensornet.general_state.tensor_network_convert import ( # type: ignore - tk_to_tensor_network, TensorNetwork, get_circuit_overlap, get_operator_expectation_value, + tk_to_tensor_network, ) from pytket.pauli import Pauli, QubitPauliString from pytket.utils.operators import QubitPauliOperator -def state_contract(tn: List[Union[NDArray, List]]) -> NDArray: +def state_contract(tn: list[Union[NDArray, list]]) -> NDArray: """Calls cuQuantum contract function to contract an input state tensor network.""" state_tn = tn.copy() state: NDArray = cq.contract(*state_tn).flatten() diff --git a/tests/test_utils.py b/tests/test_utils.py index fa6c68b2..8059afa5 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,10 +1,11 @@ -import pytest import numpy +import pytest + +from pytket import Circuit, Qubit # type: ignore from pytket.extensions.cutensornet.general import CuTensorNetHandle, set_logger from pytket.extensions.cutensornet.general_state.utils import ( circuit_statevector_postselect, ) -from pytket import Circuit, Qubit # type: ignore def test_circuit_statevector_postselect() -> None: @@ -39,5 +40,5 @@ def test_device_properties_logger() -> None: try: with CuTensorNetHandle() as libhandle: libhandle.print_device_properties(set_logger("GeneralState", 10)) - except: + except: # noqa: E722 pytest.fail("Could not print device properties")