diff --git a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py index c107a6061a..4bc0d70d13 100644 --- a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py @@ -15,7 +15,7 @@ import itertools import logging from copy import deepcopy -from typing import List, Optional, Union, cast, Dict +from typing import Dict, List, Optional, Union, cast import numpy as np @@ -317,7 +317,7 @@ class Z2Symmetries: and stacked_symmetries[row, col + symm_shape[1] // 2] == 1 ): sq_paulis.append( - Pauli(np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2)) + Pauli((np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2))) ) sq_paulis[row].z[col] = True sq_paulis[row].x[col] = False @@ -347,7 +347,7 @@ class Z2Symmetries: and stacked_symmetries[row, col + symm_shape[1] // 2] == 0 ): sq_paulis.append( - Pauli(np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2)) + Pauli((np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2))) ) sq_paulis[row].z[col] = True sq_paulis[row].x[col] = True diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 60c52ee329..e04bf37f8d 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -32,7 +32,6 @@ Operators PauliTable StabilizerTable pauli_basis - pauli_group States ====== @@ -124,7 +123,7 @@ Synthesis """ from .operators import Operator, ScalarOp, Pauli, Clifford, SparsePauliOp -from .operators import PauliList, PauliTable, StabilizerTable, pauli_basis, pauli_group +from .operators import PauliList, PauliTable, StabilizerTable, pauli_basis from .operators.channel import Choi, SuperOp, Kraus, Stinespring, Chi, PTM from .operators.measures import process_fidelity, average_gate_fidelity, gate_error, diamond_norm from .operators.dihedral import CNOTDihedral diff --git a/qiskit/quantum_info/operators/__init__.py b/qiskit/quantum_info/operators/__init__.py index b16bd929b9..88c08221dc 100644 --- a/qiskit/quantum_info/operators/__init__.py +++ b/qiskit/quantum_info/operators/__init__.py @@ -12,11 +12,22 @@ """Quantum Operators.""" +from .channel import PTM, Chi, Choi, Kraus, Stinespring, SuperOp +from .dihedral import CNOTDihedral +from .measures import ( + average_gate_fidelity, + diamond_norm, + gate_error, + process_fidelity, +) from .operator import Operator from .scalar_op import ScalarOp -from .channel import Choi, SuperOp, Kraus, Stinespring, Chi, PTM -from .measures import process_fidelity, average_gate_fidelity, gate_error, diamond_norm -from .symplectic import Clifford, Pauli, PauliList, SparsePauliOp, PauliTable, StabilizerTable -from .symplectic import pauli_basis -from .pauli import pauli_group -from .dihedral import CNOTDihedral +from .symplectic import ( + Clifford, + Pauli, + PauliList, + PauliTable, + SparsePauliOp, + StabilizerTable, + pauli_basis, +) diff --git a/qiskit/quantum_info/operators/dihedral/dihedral.py b/qiskit/quantum_info/operators/dihedral/dihedral.py index a2786f94fd..c465389329 100644 --- a/qiskit/quantum_info/operators/dihedral/dihedral.py +++ b/qiskit/quantum_info/operators/dihedral/dihedral.py @@ -19,9 +19,8 @@ import numpy as np from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info.operators.operator import Operator -from qiskit.quantum_info.operators.pauli import Pauli +from qiskit.quantum_info.operators.symplectic.pauli import Pauli from qiskit.quantum_info.operators.scalar_op import ScalarOp -from qiskit.quantum_info.synthesis.cnotdihedral_decompose import decompose_cnotdihedral from qiskit.quantum_info.operators.mixins import generate_apidocs, AdjointMixin from qiskit.circuit import QuantumCircuit, Instruction from .dihedral_circuits import _append_circuit @@ -314,6 +313,8 @@ class CNOTDihedral(BaseOperator, AdjointMixin): *Scalable randomised benchmarking of non-Clifford gates*, npj Quantum Inf 2, 16012 (2016). """ + from qiskit.quantum_info.synthesis.cnotdihedral_decompose import decompose_cnotdihedral + return decompose_cnotdihedral(self) def to_instruction(self): diff --git a/qiskit/quantum_info/operators/pauli.py b/qiskit/quantum_info/operators/pauli.py index 6f085a2baf..a1e2797b86 100644 --- a/qiskit/quantum_info/operators/pauli.py +++ b/qiskit/quantum_info/operators/pauli.py @@ -10,74 +10,26 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. +# pylint: disable=unused-import + """ DEPRECATED Tools for working with Pauli Operators. """ -from warnings import warn -import numpy as np -from qiskit.exceptions import QiskitError -from qiskit.quantum_info.operators.symplectic.pauli import Pauli +import warnings -def pauli_group(number_of_qubits, case="weight"): - """DEPRECATED: Return the Pauli group with 4^n elements. +def __getattr__(name): + if name == "Pauli": + from qiskit.quantum_info import Pauli - This function is deprecated. Use :func:`~qiskit.quantum_info.pauli_basis` - for equivalent functionality. + warnings.warn( + f"Importing from '{__name__}' is deprecated since Qiskit Terra 0.21 and the module" + " will be removed in a future release. Import directly from 'qiskit.quantum_info'.", + category=DeprecationWarning, + stacklevel=2, + ) - The phases have been removed. - case 'weight' is ordered by Pauli weights and - case 'tensor' is ordered by I,X,Y,Z counting lowest qubit fastest. - - Args: - number_of_qubits (int): number of qubits - case (str): determines ordering of group elements ('weight' or 'tensor') - - Returns: - list: list of Pauli objects - - Raises: - QiskitError: case is not 'weight' or 'tensor' - QiskitError: number_of_qubits is larger than 4 - """ - warn( - "`insert_paulis` is deprecated and will be removed no earlier than " - "3 months after the release date. For equivalent functionality to" - "`qiskit.quantum_info.pauli_group` instead. " - "`pauli_group(n)` is equivalent to `pauli_basis(n, weight=True)`, " - '`pauli_group(n, case="tensor") is equivalent to `pauli_basis(n)`', - DeprecationWarning, - stacklevel=2, - ) - if number_of_qubits < 5: - temp_set = [] - - if case == "weight": - tmp = pauli_group(number_of_qubits, case="tensor") - # sort on the weight of the Pauli operator - return sorted(tmp, key=lambda x: -np.count_nonzero(np.array(x.to_label(), "c") == b"I")) - elif case == "tensor": - # the Pauli set is in tensor order II IX IY IZ XI ... - for k in range(4**number_of_qubits): - z = np.zeros(number_of_qubits, dtype=bool) - x = np.zeros(number_of_qubits, dtype=bool) - # looping over all the qubits - for j in range(number_of_qubits): - # making the Pauli for each j fill it in from the - # end first - element = (k // (4**j)) % 4 - if element == 1: - x[j] = True - elif element == 2: - z[j] = True - x[j] = True - elif element == 3: - z[j] = True - temp_set.append(Pauli(z, x)) - return temp_set - else: - raise QiskitError(f"Only support 'weight' or 'tensor' cases but you have {case}.") - - raise QiskitError("Only support number of qubits is less than 5") + return Pauli + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/qiskit/quantum_info/operators/symplectic/clifford.py b/qiskit/quantum_info/operators/symplectic/clifford.py index 89abd95289..cbfb5c7337 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford.py +++ b/qiskit/quantum_info/operators/symplectic/clifford.py @@ -21,7 +21,6 @@ from qiskit.circuit.library.standard_gates import IGate, XGate, YGate, ZGate, HG from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info.operators.operator import Operator from qiskit.quantum_info.operators.scalar_op import ScalarOp -from qiskit.quantum_info.synthesis.clifford_decompose import decompose_clifford from qiskit.quantum_info.operators.mixins import generate_apidocs, AdjointMixin from qiskit.quantum_info.operators.symplectic.base_pauli import _count_y from .stabilizer_table import StabilizerTable @@ -384,6 +383,8 @@ class Clifford(BaseOperator, AdjointMixin): Phys. Rev. A 70, 052328 (2004). `arXiv:quant-ph/0406196 `_ """ + from qiskit.quantum_info.synthesis.clifford_decompose import decompose_clifford + return decompose_clifford(self) def to_instruction(self): diff --git a/qiskit/quantum_info/operators/symplectic/pauli.py b/qiskit/quantum_info/operators/symplectic/pauli.py index 14e20b61e3..941ae4d522 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli.py +++ b/qiskit/quantum_info/operators/symplectic/pauli.py @@ -13,9 +13,9 @@ N-qubit Pauli Operator Class """ # pylint: disable=invalid-name -# pylint: disable=bad-docstring-quotes # for deprecate_function decorator import re +import warnings from typing import Dict import numpy as np @@ -29,7 +29,6 @@ from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.mixins import generate_apidocs from qiskit.quantum_info.operators.scalar_op import ScalarOp from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli, _count_y -from qiskit.utils.deprecation import deprecate_function class Pauli(BasePauli): @@ -167,9 +166,6 @@ class Pauli(BasePauli): characters from ['+', '-', ''], ['1', ''], and ['i', 'j', ''] in this order, e.g. '', '-1j' while a Pauli string is 1 or more characters of 'I', 'X', 'Y' or 'Z', e.g. 'Z', 'XIYY'. - x (np.ndarray): DEPRECATED, symplectic x vector. - z (np.ndarray): DEPRECATED, symplectic z vector. - label (str): DEPRECATED, string label. Raises: QiskitError: if input array is invalid shape. @@ -188,13 +184,27 @@ class Pauli(BasePauli): base_z, base_x, base_phase = self._from_scalar_op(data) elif isinstance(data, (QuantumCircuit, Instruction)): base_z, base_x, base_phase = self._from_circuit(data) - elif x is not None: # DEPRECATED + elif x is not None: if z is None: # Using old Pauli initialization with positional args instead of kwargs z = data - base_z, base_x, base_phase = self._from_array_deprecated(z, x) - elif label is not None: # DEPRECATED - base_z, base_x, base_phase = self._from_label_deprecated(label) + warnings.warn( + "Passing 'z' and 'x' arrays separately to 'Pauli' is deprecated as of" + " Qiskit Terra 0.17 and will be removed in version 0.23 or later." + " Use a tuple instead, such as 'Pauli((z, x[, phase]))'.", + DeprecationWarning, + stacklevel=2, + ) + base_z, base_x, base_phase = self._from_array(z, x) + elif label is not None: + warnings.warn( + "The 'label' keyword argument of 'Pauli' is deprecated as of" + " Qiskit Terra 0.17 and will be removed in version 0.23 or later." + " Pass the label positionally instead, such as 'Pauli(\"XYZ\")'.", + DeprecationWarning, + stacklevel=2, + ) + base_z, base_x, base_phase = self._from_label(label) else: raise QiskitError("Invalid input data for Pauli.") @@ -687,353 +697,6 @@ class Pauli(BasePauli): ret = ret.compose(next_instr, qargs=qargs) return ret._z, ret._x, ret._phase - # --------------------------------------------------------------------- - # DEPRECATED methods from old Pauli class - # --------------------------------------------------------------------- - - @classmethod - @deprecate_function( - "Initializing Pauli from `Pauli(label=l)` kwarg is deprecated as of " - "version 0.17.0 and will be removed no earlier than 3 months after " - "the release date. Use `Pauli(l)` instead." - ) - def _from_label_deprecated(cls, label): - # Deprecated wrapper of `_from_label` so that a deprecation warning - # can be displaced during initialization with deprecated kwarg - return cls._from_label(label) - - @classmethod - @deprecate_function( - "Initializing Pauli from `Pauli(z=z, x=x)` kwargs is deprecated as of " - "version 0.17.0 and will be removed no earlier than 3 months after " - "the release date. Use tuple initialization `Pauli((z, x))` instead." - ) - def _from_array_deprecated(cls, z, x): - # Deprecated wrapper of `_from_array` so that a deprecation warning - # can be displaced during initialization with deprecated kwarg - return cls._from_array(z, x) - - @staticmethod - def _make_np_bool(arr): - if not isinstance(arr, (list, np.ndarray, tuple)): - arr = [arr] - arr = np.asarray(arr).astype(bool) - return arr - - @staticmethod - @deprecate_function( - "`from_label` is deprecated and will be removed no earlier than " - "3 months after the release date. Use Pauli(label) instead." - ) - def from_label(label): - """DEPRECATED: Construct a Pauli from a string label. - - This function is deprecated use ``Pauli(label)`` instead. - - Args: - label (str): Pauli string label. - - Returns: - Pauli: the constructed Pauli. - - Raises: - QiskitError: If the input list is empty or contains invalid - Pauli strings. - """ - if isinstance(label, tuple): - # Legacy usage from aqua - label = "".join(label) - return Pauli(label) - - @staticmethod - @deprecate_function( - "sgn_prod is deprecated and will be removed no earlier than " - "3 months after the release date. Use `dot` instead." - ) - def sgn_prod(p1, p2): - r""" - DEPRECATED: Multiply two Paulis and track the phase. - - This function is deprecated. The Pauli class now handles full - Pauli group multiplication using :meth:`compose` or :meth:`dot`. - - $P_3 = P_1 \otimes P_2$: X*Y - - Args: - p1 (Pauli): pauli 1 - p2 (Pauli): pauli 2 - - Returns: - Pauli: the multiplied pauli (without phase) - complex: the sign of the multiplication, 1, -1, 1j or -1j - """ - pauli = p1.dot(p2) - return pauli[:], (-1j) ** pauli.phase - - @deprecate_function( - "`to_spmatrix` is deprecated and will be removed no earlier than " - "3 months after the release date. Use `to_matrix(sparse=True)` instead." - ) - def to_spmatrix(self): - r""" - DEPRECATED Convert Pauli to a sparse matrix representation (CSR format). - - This function is deprecated. Use :meth:`to_matrix` with kwarg - ``sparse=True`` instead. - - Returns: - scipy.sparse.csr_matrix: a sparse matrix with CSR format that - represents the pauli. - """ - return self.to_matrix(sparse=True) - - @deprecate_function( - "`kron` is deprecated and will be removed no earlier than " - "3 months after the release date of Qiskit Terra 0.17.0. " - "Use `expand` instead, but note this does not change " - "the operator in-place." - ) - def kron(self, other): - r"""DEPRECATED: Kronecker product of two paulis. - - This function is deprecated. Use :meth:`expand` instead. - - Order is $P_2 (other) \otimes P_1 (self)$ - - Args: - other (Pauli): P2 - - Returns: - Pauli: self - """ - pauli = self.expand(other) - self._z = pauli._z - self._x = pauli._x - self._phase = pauli._phase - self._op_shape = self._op_shape.expand(other._op_shape) - return self - - @deprecate_function( - "`update_z` is deprecated and will be removed no earlier than " - "3 months after the release date. Use `Pauli.z = val` or " - "`Pauli.z[indices] = val` instead." - ) - def update_z(self, z, indices=None): - """ - DEPRECATED: Update partial or entire z. - - This function is deprecated. Use the setter for :attr:`Z` instead. - - Args: - z (numpy.ndarray or list): to-be-updated z - indices (numpy.ndarray or list or optional): to-be-updated qubit indices - - Returns: - Pauli: self - - Raises: - QiskitError: when updating whole z, the number of qubits must be the same. - """ - phase = self.phase - z = self._make_np_bool(z) - if indices is None: - if len(self.z) != len(z): - raise QiskitError( - "During updating whole z, you can not change the number of qubits." - ) - self.z = z - else: - if not isinstance(indices, list) and not isinstance(indices, np.ndarray): - indices = [indices] - for p, idx in enumerate(indices): - self.z[idx] = z[p] - self.phase = phase - return self - - @deprecate_function( - "`update_z` is deprecated and will be removed no earlier than " - "3 months after the release date. Use `Pauli.x = val` or " - "`Pauli.x[indices] = val` instead." - ) - def update_x(self, x, indices=None): - """ - DEPRECATED: Update partial or entire x. - - This function is deprecated. Use the setter for :attr:`X` instead. - - Args: - x (numpy.ndarray or list): to-be-updated x - indices (numpy.ndarray or list or optional): to-be-updated qubit indices - - Returns: - Pauli: self - - Raises: - QiskitError: when updating whole x, the number of qubits must be the same. - """ - phase = self.phase - x = self._make_np_bool(x) - if indices is None: - if len(self.x) != len(x): - raise QiskitError( - "During updating whole x, you can not change the number of qubits." - ) - self.x = x - else: - if not isinstance(indices, list) and not isinstance(indices, np.ndarray): - indices = [indices] - for p, idx in enumerate(indices): - self.x[idx] = x[p] - self.phase = phase - return self - - @deprecate_function( - "`insert_paulis` is deprecated and will be removed no earlier than " - "3 months after the release date. For similar functionality use " - "`Pauli.insert` instead." - ) - def insert_paulis(self, indices=None, paulis=None, pauli_labels=None): - """ - DEPRECATED: Insert or append pauli to the targeted indices. - - This function is deprecated. Similar functionality can be obtained - using the :meth:`insert` method. - - If indices is None, it means append at the end. - - Args: - indices (list[int]): the qubit indices to be inserted - paulis (Pauli): the to-be-inserted or appended pauli - pauli_labels (list[str]): the to-be-inserted or appended pauli label - - Note: - the indices refers to the location of original paulis, - e.g. if indices = [0, 2], pauli_labels = ['Z', 'I'] and original pauli = 'ZYXI' - the pauli will be updated to ZY'I'XI'Z' - 'Z' and 'I' are inserted before the qubit at 0 and 2. - - Returns: - Pauli: self - - Raises: - QiskitError: provide both `paulis` and `pauli_labels` at the same time - """ - if pauli_labels is not None: - if paulis is not None: - raise QiskitError("Please only provide either `paulis` or `pauli_labels`") - if isinstance(pauli_labels, str): - pauli_labels = list(pauli_labels) - # since pauli label is in reversed order. - label = "".join(pauli_labels[::-1]) - paulis = self.from_label(label) - - # Insert and update self - if indices is None: # append - z = np.concatenate((self.z, paulis.z)) - x = np.concatenate((self.x, paulis.x)) - else: - if not isinstance(indices, list): - indices = [indices] - z = np.insert(self.z, indices, paulis.z) - x = np.insert(self.x, indices, paulis.x) - pauli = Pauli((z, x, self.phase + paulis.phase)) - self._z = pauli._z - self._x = pauli._x - self._phase = pauli._phase - self._op_shape = pauli._op_shape - return self - - @deprecate_function( - "`append_paulis` is deprecated and will be removed no earlier than " - "3 months after the release date. Use `Pauli.expand` instead." - ) - def append_paulis(self, paulis=None, pauli_labels=None): - """ - DEPRECATED: Append pauli at the end. - - Args: - paulis (Pauli): the to-be-inserted or appended pauli - pauli_labels (list[str]): the to-be-inserted or appended pauli label - - Returns: - Pauli: self - """ - return self.insert_paulis(None, paulis=paulis, pauli_labels=pauli_labels) - - @deprecate_function( - "`append_paulis` is deprecated and will be removed no earlier than " - "3 months after the release date. For equivalent functionality " - "use `Pauli.delete` instead." - ) - def delete_qubits(self, indices): - """ - DEPRECATED: Delete pauli at the indices. - - This function is deprecated. Equivalent functionality can be obtained - using the :meth:`delete` method. - - Args: - indices(list[int]): the indices of to-be-deleted paulis - - Returns: - Pauli: self - """ - pauli = self.delete(indices) - self._z = pauli._z - self._x = pauli._x - self._phase = pauli._phase - self._op_shape = pauli._op_shape - return self - - @classmethod - @deprecate_function( - "`pauli_single` is deprecated and will be removed no earlier than " - "3 months after the release date." - ) - def pauli_single(cls, num_qubits, index, pauli_label): - """ - DEPRECATED: Generate single qubit pauli at index with pauli_label with length num_qubits. - - Args: - num_qubits (int): the length of pauli - index (int): the qubit index to insert the single qubit - pauli_label (str): pauli - - Returns: - Pauli: single qubit pauli - """ - tmp = Pauli(pauli_label) - ret = Pauli((np.zeros(num_qubits, dtype=bool), np.zeros(num_qubits, dtype=bool))) - ret.x[index] = tmp.x[0] - ret.z[index] = tmp.z[0] - ret.phase = tmp.phase - return ret - - @classmethod - @deprecate_function( - "`random` is deprecated and will be removed no earlier than " - "3 months after the release date. " - "Use `qiskit.quantum_info.random_pauli` instead" - ) - def random(cls, num_qubits, seed=None): - """DEPRECATED: Return a random Pauli on number of qubits. - - This function is deprecated use - :func:`~qiskit.quantum_info.random_pauli` instead. - - Args: - num_qubits (int): the number of qubits - seed (int): Optional. To set a random seed. - Returns: - Pauli: the random pauli - """ - # pylint: disable=cyclic-import - from qiskit.quantum_info.operators.symplectic.random import ( - random_pauli, - ) - - return random_pauli(num_qubits, group_phase=False, seed=seed) - # --------------------------------------------------------------------- # Label parsing helper functions diff --git a/releasenotes/notes/remove-deprecate-calsses-methods-7bd69606cc4ad61f.yaml b/releasenotes/notes/remove-deprecate-calsses-methods-7bd69606cc4ad61f.yaml new file mode 100644 index 0000000000..2e470a9baf --- /dev/null +++ b/releasenotes/notes/remove-deprecate-calsses-methods-7bd69606cc4ad61f.yaml @@ -0,0 +1,47 @@ +--- +upgrade: + - | + The deprecated function ``qiskit.quantum_info.pauli_group()`` has been removed. + It was originally deprecated in Qiskit Terra 0.17. + - | + Several deprecated methods on :class:`.Pauli` have been removed, which were + originally deprecated in Qiskit Terra 0.17. These were: + + ``sgn_prod`` + Use :meth:`.Pauli.compose` or :meth:`.Pauli.dot` instead. + + ``to_spmatrix`` + Use :meth:`.Pauli.to_matrix` with argument ``sparse=True`` instead. + + ``kron`` + Use :meth:`.Pauli.expand`, but beware that this returns a new object, rather + than mutating the existing one. + + ``update_z`` and ``update_x`` + Set the ``z`` and ``x`` attributes of the object directly. + + ``insert_paulis`` + Use :meth:`.Pauli.insert`. + + ``append_paulis`` + Use :meth:`.Pauli.expand`. + + ``delete_qubits`` + Use :meth:`.Pauli.delete`. + + ``pauli_single`` + Construct the label manually and pass directly to the initializer, such as:: + + Pauli("I" * index + pauli_label + "I" * (num_qubits - index - len(pauli_label))) + + ``random`` + Use :func:`.quantum_info.random_pauli` instead. +deprecations: + - | + The arguments ``x``, ``z`` and ``label`` to the initializer of + :class:`.Pauli` were documented as deprecated in Qiskit Terra 0.17, but a bug + prevented the expected warning from being shown at runtime. The warning will + now correctly show, and the arguments will be removed in Qiskit Terra 0.23 or + later. A pair of ``x`` and ``z`` should be passed positionally as a single + tuple (``Pauli((z, x))``). A string ``label`` should be passed positionally + in the first argument (``Pauli("XYZ")``). diff --git a/test/python/quantum_info/operators/symplectic/test_legacy_pauli.py b/test/python/quantum_info/operators/symplectic/test_legacy_pauli.py deleted file mode 100644 index 27b3262e11..0000000000 --- a/test/python/quantum_info/operators/symplectic/test_legacy_pauli.py +++ /dev/null @@ -1,741 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Quick program to test the qi tools modules.""" - -import unittest -from copy import deepcopy -import numpy as np - -from qiskit.quantum_info.operators import Pauli -from qiskit.quantum_info import pauli_group -from qiskit.test import QiskitTestCase - - -class TestPauliAPI(QiskitTestCase): - """Tests for Pauli class API.""" - - def check(self, result): - """checks for result to be a Pauli 'IY'""" - self.assertIsInstance(result, Pauli) - self.assertEqual(result.num_qubits, 2) - self.assertEqual(result.to_label(), "IY") - - def test_ndarray_bool(self): - """Test creation from bool.""" - x = np.asarray([1, 0]).astype(bool) - z = np.asarray([1, 0]).astype(bool) - pauli = Pauli(x=x, z=z) - self.check(pauli) - - def test_ndarray_int(self): - """Test creation from int.""" - x = np.asarray([2, 0]).astype(int) - z = np.asarray([2, 0]).astype(int) - pauli = Pauli(x=x, z=z) - self.check(pauli) - - def test_list(self): - """Test creation from lists.""" - pauli = Pauli(x=[1, 0], z=[1, 0]) - self.check(pauli) - - def test_tuple(self): - """Test creation from tuples.""" - pauli = Pauli(x=(1, 0), z=(1, 0)) - self.check(pauli) - - def test_mix(self): - """Test creation from tuples and list.""" - pauli = Pauli(x=(1, 0), z=[1, 0]) - self.check(pauli) - - -class TestPauli(QiskitTestCase): - """Tests for Pauli class.""" - - def setUp(self): - """Setup.""" - super().setUp() - z = np.asarray([1, 0, 1, 0]).astype(bool) - x = np.asarray([1, 1, 0, 0]).astype(bool) - self.ref_p = Pauli(z, x) - self.ref_label = "IZXY" - self.ref_matrix = np.array( - [ - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - [ - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 - 1.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - 0.0 + 0.0j, - ], - ] - ) - - def test_create_from_label(self): - """Test creation from pauli label.""" - label = "IZXY" - pauli = Pauli(label=label) - - self.assertEqual(pauli, self.ref_p) - self.assertEqual(pauli.to_label(), self.ref_label) - self.assertEqual(len(pauli), 4) - - def test_create_from_z_x(self): - """Test creation for boolean vector.""" - self.assertEqual(self.ref_p.to_label(), "IZXY") - self.assertEqual(len(self.ref_p), 4) - - def test_random_pauli(self): - """Test random pauli creation.""" - length = 4 - q = Pauli.random(length, seed=42) - self.log.info(q) - self.assertEqual(q.num_qubits, length) - self.assertEqual(len(q.z), length) - self.assertEqual(len(q.x), length) - self.assertEqual(len(q.to_label()), length) - self.assertEqual(len(q.to_matrix()), 2**length) - - def test_mul(self): - """Test multiplication.""" - p1 = self.ref_p - p2 = Pauli.from_label("ZXXI") - p3 = p1.dot(p2) - self.assertEqual(len(p3), 4) - self.assertEqual(p3[:].to_label(), "ZYIY") - - def test_imul(self): - """Test in-place multiplication.""" - p1 = self.ref_p - p2 = Pauli.from_label("ZXXI") - p3 = deepcopy(p2) - p2 *= p1 - self.assertTrue(p2 != p3) - self.assertEqual(p2[:].to_label(), "ZYIY") - - def test_equality_equal(self): - """Test equality operator: equal Paulis.""" - p1 = self.ref_p - p2 = deepcopy(p1) - self.assertTrue(p1 == p2) - self.assertEqual(p1.to_label(), self.ref_label) - self.assertEqual(p2.to_label(), self.ref_label) - - def test_equality_different(self): - """Test equality operator: different Paulis.""" - p1 = self.ref_p - p2 = deepcopy(p1) - - p2.update_z(True, 1) - self.assertFalse(p1 == p2) - self.assertEqual(p1.to_label(), self.ref_label) - self.assertEqual(p2.to_label(), "IZYY") - - def test_inequality_equal(self): - """Test inequality operator: equal Paulis.""" - p1 = self.ref_p - p2 = deepcopy(p1) - - self.assertFalse(p1 != p2) - - def test_inequality_different(self): - """Test inequality operator: different Paulis.""" - p1 = self.ref_p - p2 = deepcopy(p1) - p2.update_x(False, 1) - self.assertTrue(p1 != p2) - self.assertEqual(p2.to_label(), "IZIY") - - def test_update_z(self): - """Test update_z method.""" - updated_z = np.asarray([0, 0, 0, 0]).astype(bool) - self.ref_p.update_z(updated_z) - np.testing.assert_equal(self.ref_p.z, np.asarray([False, False, False, False])) - self.assertEqual(self.ref_p.to_label(), "IIXX") - - def test_update_z_2(self): - """Test update_z method, update partial z.""" - updated_z = np.asarray([0, 1]).astype(bool) - self.ref_p.update_z(updated_z, [0, 1]) - np.testing.assert_equal(self.ref_p.z, np.asarray([False, True, True, False])) - self.assertEqual(self.ref_p.to_label(), "IZYX") - - def test_update_x(self): - """Test update_x method.""" - updated_x = np.asarray([0, 1, 0, 1]).astype(bool) - self.ref_p.update_x(updated_x) - np.testing.assert_equal(self.ref_p.x, np.asarray([False, True, False, True])) - self.assertEqual(self.ref_p.to_label(), "XZXZ") - - def test_update_x_2(self): - """Test update_x method, update partial x.""" - updated_x = np.asarray([0, 1]).astype(bool) - self.ref_p.update_x(updated_x, [1, 2]) - np.testing.assert_equal(self.ref_p.x, np.asarray([True, False, True, False])) - self.assertEqual(self.ref_p.to_label(), "IYIY") - - def test_to_matrix(self): - """Test pauli to matrix.""" - np.testing.assert_allclose(self.ref_p.to_matrix(), self.ref_matrix) - - def test_delete_qubit(self): - """Test deleting single qubit.""" - p1 = self.ref_p - p2 = deepcopy(p1) - - p2.delete_qubits(0) - self.assertTrue(p1 != p2) - self.assertEqual(len(p2), 3) - self.assertEqual(p2.to_label(), "IZX") - - def test_delete_qubits(self): - """Test deleting multiple qubits.""" - p1 = self.ref_p - p2 = deepcopy(p1) - - p2.delete_qubits([0, 2]) - self.assertTrue(p1 != p2) - self.assertEqual(len(p2), 2) - self.assertEqual(p2.to_label(), "IX") - - def test_append_pauli_labels(self): - """Test appending paulis via labels.""" - p1 = self.ref_p - p2 = deepcopy(p1) - - p2.append_paulis(pauli_labels=["Z", "Y", "I"]) - self.assertTrue(p1 != p2) - self.assertEqual(len(p2), 7) - self.assertEqual(p2.to_label(), "IYZ" + self.ref_label) - - def test_append_paulis(self): - """Test appending paulis via pauli object.""" - p1 = self.ref_p - p2 = deepcopy(p1) - - p2.append_paulis(paulis=p1) - self.assertTrue(p1 != p2) - self.assertEqual(len(p2), 8) - self.assertEqual(p2.to_label(), self.ref_label + self.ref_label) - - def test_insert_pauli_labels_1(self): - """Test inserting paulis via labels.""" - p2 = deepcopy(self.ref_p) - - p2.insert_paulis(indices=[1, 2], pauli_labels=["Y", "I"]) - self.assertTrue(self.ref_p != p2) - self.assertEqual(len(p2), 6) - self.assertEqual(p2.to_label(), "IZIXYY") - - def test_insert_pauli_labels_2(self): - """Test inserting paulis via labels.""" - p2 = deepcopy(self.ref_p) - - p2.insert_paulis(indices=[3, 2], pauli_labels=["Y", "I"]) - self.assertTrue(self.ref_p != p2) - self.assertEqual(len(p2), 6) - self.assertEqual(p2.to_label(), "IYZIXY") - - def test_insert_paulis(self): - """Test inserting paulis via pauli object.""" - p1 = deepcopy(self.ref_p) - - new_p = Pauli.from_label("XY") - - p1.insert_paulis(indices=[0], paulis=new_p) - - self.assertTrue(p1 != self.ref_p) - self.assertEqual(len(p1), 6) - self.assertEqual(p1.to_label(), self.ref_label + "XY") - - def test_kron(self): - """Test kron production.""" - p1 = deepcopy(self.ref_p) - p2 = deepcopy(self.ref_p) - p2.kron(p1) - self.assertTrue(p1 != p2) - self.assertEqual(len(p2), 8) - self.assertEqual(p2.to_label(), self.ref_label + self.ref_label) - - def test_pauli_single(self): - """Test pauli single.""" - num_qubits = 5 - pz = Pauli.pauli_single(num_qubits, 2, "Z") - self.assertTrue(pz.to_label(), "IIIZI") - - py = Pauli.pauli_single(num_qubits, 4, "Y") - self.assertTrue(py.to_label(), "IYIII") - - px = Pauli.pauli_single(num_qubits, 3, "X") - self.assertTrue(px.to_label(), "IIXII") - - def test_pauli_group(self): - """Test pauli group.""" - self.log.info("Group in tensor order:") - expected = [ - "III", - "XII", - "YII", - "ZII", - "IXI", - "XXI", - "YXI", - "ZXI", - "IYI", - "XYI", - "YYI", - "ZYI", - "IZI", - "XZI", - "YZI", - "ZZI", - "IIX", - "XIX", - "YIX", - "ZIX", - "IXX", - "XXX", - "YXX", - "ZXX", - "IYX", - "XYX", - "YYX", - "ZYX", - "IZX", - "XZX", - "YZX", - "ZZX", - "IIY", - "XIY", - "YIY", - "ZIY", - "IXY", - "XXY", - "YXY", - "ZXY", - "IYY", - "XYY", - "YYY", - "ZYY", - "IZY", - "XZY", - "YZY", - "ZZY", - "IIZ", - "XIZ", - "YIZ", - "ZIZ", - "IXZ", - "XXZ", - "YXZ", - "ZXZ", - "IYZ", - "XYZ", - "YYZ", - "ZYZ", - "IZZ", - "XZZ", - "YZZ", - "ZZZ", - ] - grp = pauli_group(3, case="tensor") - for j in grp: - self.log.info("==== j (tensor order) ====") - self.log.info(j.to_label()) - self.assertEqual(expected.pop(0)[::-1], j.to_label()) - - self.log.info("Group in weight order:") - expected = [ - "III", - "XII", - "YII", - "ZII", - "IXI", - "IYI", - "IZI", - "IIX", - "IIY", - "IIZ", - "XXI", - "YXI", - "ZXI", - "XYI", - "YYI", - "ZYI", - "XZI", - "YZI", - "ZZI", - "XIX", - "YIX", - "ZIX", - "IXX", - "IYX", - "IZX", - "XIY", - "YIY", - "ZIY", - "IXY", - "IYY", - "IZY", - "XIZ", - "YIZ", - "ZIZ", - "IXZ", - "IYZ", - "IZZ", - "XXX", - "YXX", - "ZXX", - "XYX", - "YYX", - "ZYX", - "XZX", - "YZX", - "ZZX", - "XXY", - "YXY", - "ZXY", - "XYY", - "YYY", - "ZYY", - "XZY", - "YZY", - "ZZY", - "XXZ", - "YXZ", - "ZXZ", - "XYZ", - "YYZ", - "ZYZ", - "XZZ", - "YZZ", - "ZZZ", - ] - grp = pauli_group(3, case="weight") - for j in grp: - self.log.info("==== j (weight order) ====") - self.log.info(j.to_label()) - self.assertEqual(expected.pop(0)[::-1], j.to_label()) - - def test_sgn_prod(self): - """Test sgn prod.""" - p1 = Pauli(np.array([False]), np.array([True])) - p2 = Pauli(np.array([True]), np.array([True])) - - self.log.info("sign product:") - p3, sgn = Pauli.sgn_prod(p1, p2) - self.log.info("p1: %s", p1.to_label()) - self.log.info("p2: %s", p2.to_label()) - self.log.info("p3: %s", p3.to_label()) - self.log.info("sgn_prod(p1, p2): %s", str(sgn)) - self.assertEqual(p1.to_label(), "X") - self.assertEqual(p2.to_label(), "Y") - self.assertEqual(p3.to_label(), "Z") - self.assertEqual(sgn, 1j) - - self.log.info("sign product reverse:") - p3, sgn = Pauli.sgn_prod(p2, p1) # pylint: disable=arguments-out-of-order - self.log.info("p2: %s", p2.to_label()) - self.log.info("p1: %s", p1.to_label()) - self.log.info("p3: %s", p3.to_label()) - self.log.info("sgn_prod(p2, p1): %s", str(sgn)) - self.assertEqual(p1.to_label(), "X") - self.assertEqual(p2.to_label(), "Y") - self.assertEqual(p3.to_label(), "Z") - self.assertEqual(sgn, -1j) - - -if __name__ == "__main__": - unittest.main()