Ejemplo n.º 1
0
    def __init__(self, data, coeffs=None):
        """Initialize an operator object.

        Args:
            data (Paulilist, SparsePauliOp, PauliTable): Pauli list of terms.
            coeffs (np.ndarray): complex coefficients for Pauli terms.

        Raises:
            QiskitError: If the input data or coeffs are invalid.
        """
        if isinstance(data, SparsePauliOp):
            pauli_list = data._pauli_list
            coeffs = data._coeffs
        else:
            pauli_list = PauliList(data)
            if coeffs is None:
                coeffs = np.ones(pauli_list.size, dtype=complex)
        # Initialize PauliList
        self._pauli_list = PauliList.from_symplectic(pauli_list.z, pauli_list.x)

        # Initialize Coeffs
        self._coeffs = np.asarray((-1j) ** pauli_list.phase * coeffs, dtype=complex)
        if self._coeffs.shape != (self._pauli_list.size,):
            raise QiskitError(
                "coeff vector is incorrect shape for number"
                " of Paulis {} != {}".format(self._coeffs.shape, self._pauli_list.size)
            )
        # Initialize LinearOp
        super().__init__(num_qubits=self._pauli_list.num_qubits)
Ejemplo n.º 2
0
    def simplify(self, atol=None, rtol=None):
        """Simplify PauliList by combining duplicates and removing zeros.

        Args:
            atol (float): Optional. Absolute tolerance for checking if
                          coefficients are zero (Default: 1e-8).
            rtol (float): Optional. relative tolerance for checking if
                          coefficients are zero (Default: 1e-5).

        Returns:
            SparsePauliOp: the simplified SparsePauliOp operator.
        """
        # Get default atol and rtol
        if atol is None:
            atol = self.atol
        if rtol is None:
            rtol = self.rtol

        # Filter non-zero coefficients
        non_zero = np.logical_not(
            np.isclose(self.coeffs, 0, atol=atol, rtol=rtol))
        paulis_x = self.paulis.x[non_zero]
        paulis_z = self.paulis.z[non_zero]
        nz_coeffs = self.coeffs[non_zero]

        # Pack bool vectors into np.uint8 vectors by np.packbits
        array = np.packbits(paulis_x, axis=1) * 256 + np.packbits(paulis_z,
                                                                  axis=1)
        indexes, inverses = unordered_unique(array)

        if np.all(non_zero) and indexes.shape[0] == array.shape[0]:
            # No zero operator or duplicate operator
            return self.copy()

        coeffs = np.zeros(indexes.shape[0], dtype=complex)
        np.add.at(coeffs, inverses, nz_coeffs)
        # Delete zero coefficient rows
        is_zero = np.isclose(coeffs, 0, atol=atol, rtol=rtol)
        # Check edge case that we deleted all Paulis
        # In this case we return an identity Pauli with a zero coefficient
        if np.all(is_zero):
            x = np.zeros((1, self.num_qubits), dtype=bool)
            z = np.zeros((1, self.num_qubits), dtype=bool)
            coeffs = np.array([0j], dtype=complex)
        else:
            non_zero = np.logical_not(is_zero)
            non_zero_indexes = indexes[non_zero]
            x = paulis_x[non_zero_indexes]
            z = paulis_z[non_zero_indexes]
            coeffs = coeffs[non_zero]

        return SparsePauliOp(PauliList.from_symplectic(z, x),
                             coeffs,
                             ignore_pauli_phase=True,
                             copy=False)
Ejemplo n.º 3
0
    def compose(self, other, qargs=None, front=False):
        if qargs is None:
            qargs = getattr(other, "qargs", None)

        if not isinstance(other, SparsePauliOp):
            other = SparsePauliOp(other)

        # Validate composition dimensions and qargs match
        self._op_shape.compose(other._op_shape, qargs, front)

        x1 = np.reshape(
            np.stack(other.size * [self.paulis.x], axis=1),
            (self.size * other.size, self.num_qubits),
        )
        z1 = np.reshape(
            np.stack(other.size * [self.paulis.z], axis=1),
            (self.size * other.size, self.num_qubits),
        )
        p1 = np.reshape(
            np.stack(other.size * [self.paulis.phase], axis=1),
            self.size * other.size,
        )
        paulis1 = PauliList.from_symplectic(z1, x1, p1)
        x2 = np.reshape(
            np.stack(self.size * [other.paulis.x]), (self.size * other.size, other.num_qubits)
        )
        z2 = np.reshape(
            np.stack(self.size * [other.paulis.z]), (self.size * other.size, other.num_qubits)
        )
        p2 = np.reshape(
            np.stack(self.size * [other.paulis.phase]),
            self.size * other.size,
        )
        paulis2 = PauliList.from_symplectic(z2, x2, p2)

        pauli_list = paulis1.compose(paulis2, qargs, front)
        coeffs = np.kron(self.coeffs, other.coeffs)

        return SparsePauliOp(pauli_list, coeffs)
Ejemplo n.º 4
0
 def _multiply(self, other):
     if not isinstance(other, Number):
         raise QiskitError("other is not a number")
     if other == 0:
         # Check edge case that we deleted all Paulis
         # In this case we return an identity Pauli with a zero coefficient
         paulis = PauliList.from_symplectic(
             np.zeros((1, self.num_qubits), dtype=bool),
             np.zeros((1, self.num_qubits), dtype=bool),
         )
         coeffs = np.array([0j])
         return SparsePauliOp(paulis, coeffs)
     # Otherwise we just update the phases
     return SparsePauliOp(self.paulis, other * self.coeffs)
Ejemplo n.º 5
0
    def simplify(self, atol=None, rtol=None):
        """Simplify PauliList by combining duplicates and removing zeros.

        Args:
            atol (float): Optional. Absolute tolerance for checking if
                          coefficients are zero (Default: 1e-8).
            rtol (float): Optional. relative tolerance for checking if
                          coefficients are zero (Default: 1e-5).

        Returns:
            SparsePauliOp: the simplified SparsePauliOp operator.
        """
        # Get default atol and rtol
        if atol is None:
            atol = self.atol
        if rtol is None:
            rtol = self.rtol

        array = np.column_stack((self.paulis.x, self.paulis.z))
        flatten_paulis, indexes = np.unique(array, return_inverse=True, axis=0)
        coeffs = np.zeros(self.size, dtype=complex)
        for i, val in zip(indexes, self.coeffs):
            coeffs[i] += val
        # Delete zero coefficient rows
        # TODO: Add atol/rtol for zero comparison
        non_zero = [
            i for i in range(coeffs.size) if not np.isclose(coeffs[i], 0, atol=atol, rtol=rtol)
        ]
        # Check edge case that we deleted all Paulis
        # In this case we return an identity Pauli with a zero coefficient
        if len(non_zero) == 0:
            x = np.zeros((1, self.num_qubits), dtype=bool)
            z = np.zeros((1, self.num_qubits), dtype=bool)
            coeffs = np.array([0j], dtype=complex)
        else:
            x, z = (
                flatten_paulis[non_zero]
                .reshape((len(non_zero), 2, self.num_qubits))
                .transpose(1, 0, 2)
            )
            coeffs = coeffs[non_zero]
        return SparsePauliOp(PauliList.from_symplectic(z, x), coeffs)
Ejemplo n.º 6
0
    def chop(self, tol=1e-14):
        """Set real and imaginary parts of the coefficients to 0 if ``< tol`` in magnitude.

        For example, the operator representing ``1+1e-17j X + 1e-17 Y`` with a tolerance larger
        than ``1e-17`` will be reduced to ``1 X`` whereas :meth:`.SparsePauliOp.simplify` would
        return ``1+1e-17j X``.

        If a both the real and imaginary part of a coefficient is 0 after chopping, the
        corresponding Pauli is removed from the operator.

        Args:
            tol (float): The absolute tolerance to check whether a real or imaginary part should
                be set to 0.

        Returns:
            SparsePauliOp: This operator with chopped coefficients.
        """
        realpart_nonzero = np.abs(self.coeffs.real) > tol
        imagpart_nonzero = np.abs(self.coeffs.imag) > tol

        remaining_indices = np.logical_or(realpart_nonzero, imagpart_nonzero)
        remaining_real = realpart_nonzero[remaining_indices]
        remaining_imag = imagpart_nonzero[remaining_indices]

        if len(remaining_real) == 0:  # if no Paulis are left
            x = np.zeros((1, self.num_qubits), dtype=bool)
            z = np.zeros((1, self.num_qubits), dtype=bool)
            coeffs = np.array([0j], dtype=complex)
        else:
            coeffs = np.zeros(np.count_nonzero(remaining_indices),
                              dtype=complex)
            coeffs.real[remaining_real] = self.coeffs.real[realpart_nonzero]
            coeffs.imag[remaining_imag] = self.coeffs.imag[imagpart_nonzero]

            x = self.paulis.x[remaining_indices]
            z = self.paulis.z[remaining_indices]

        return SparsePauliOp(PauliList.from_symplectic(z, x),
                             coeffs,
                             ignore_pauli_phase=True,
                             copy=False)
Ejemplo n.º 7
0
    def _evolve_clifford(self, other, qargs=None, frame="h"):

        """Heisenberg picture evolution of a Pauli by a Clifford."""

        if frame == "s":
            adj = other
        else:
            adj = other.adjoint()

        if qargs is None:
            qargs_ = slice(None)
        else:
            qargs_ = list(qargs)

        # pylint: disable=cyclic-import
        from qiskit.quantum_info.operators.symplectic.pauli_list import PauliList

        num_paulis = self._x.shape[0]

        ret = self.copy()
        ret._x[:, qargs_] = False
        ret._z[:, qargs_] = False

        idx = np.concatenate((self._x[:, qargs_], self._z[:, qargs_]), axis=1)
        for idx_, row in zip(
            idx.T,
            PauliList.from_symplectic(z=adj.table.Z, x=adj.table.X, phase=2 * adj.table.phase),
        ):
            # most of the logic below is to properly index if self is a PauliList (2D),
            # while not trying to index if the object is just a Pauli (1D).
            if idx_.any():
                if np.sum(idx_) == num_paulis:
                    ret.compose(row, qargs=qargs, inplace=True)
                else:
                    ret[idx_] = ret[idx_].compose(row, qargs=qargs)

        return ret