Exemplo n.º 1
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)

        if qargs is not None:
            x1, z1 = self.paulis.x[:, qargs], self.paulis.z[:, qargs]
        else:
            x1, z1 = self.paulis.x, self.paulis.z
        x2, z2 = other.paulis.x, other.paulis.z
        num_qubits = other.num_qubits

        # This method is the outer version of `BasePauli.compose`.
        # `x1` and `z1` have shape `(self.size, num_qubits)`.
        # `x2` and `z2` have shape `(other.size, num_qubits)`.
        # `x1[:, no.newaxis]` results in shape `(self.size, 1, num_qubits)`.
        # `ar = ufunc(x1[:, np.newaxis], x2)` will be in shape `(self.size, other.size, num_qubits)`.
        # So, `ar.reshape((-1, num_qubits))` will be in shape `(self.size * other.size, num_qubits)`.
        # Ref: https://numpy.org/doc/stable/user/theory.broadcasting.html

        phase = np.add.outer(self.paulis._phase,
                             other.paulis._phase).reshape(-1)
        if front:
            q = np.logical_and(x1[:, np.newaxis], z2).reshape((-1, num_qubits))
        else:
            q = np.logical_and(z1[:, np.newaxis], x2).reshape((-1, num_qubits))
        # `np.mod` will be applied to `phase` in `SparsePauliOp.__init__`
        phase = phase + 2 * q.sum(axis=1, dtype=np.uint8)

        x3 = np.logical_xor(x1[:, np.newaxis], x2).reshape((-1, num_qubits))
        z3 = np.logical_xor(z1[:, np.newaxis], z2).reshape((-1, num_qubits))

        if qargs is None:
            pauli_list = PauliList(BasePauli(z3, x3, phase))
        else:
            x4 = np.repeat(self.paulis.x, other.size, axis=0)
            z4 = np.repeat(self.paulis.z, other.size, axis=0)
            x4[:, qargs] = x3
            z4[:, qargs] = z3
            pauli_list = PauliList(BasePauli(z4, x4, phase))

        # note: the following is a faster code equivalent to
        # `coeffs = np.kron(self.coeffs, other.coeffs)`
        # since `self.coeffs` and `other.coeffs` are both 1d arrays.
        coeffs = np.multiply.outer(self.coeffs, other.coeffs).ravel()
        return SparsePauliOp(pauli_list, coeffs, copy=False)
Exemplo n.º 2
0
    def sum(ops):
        """Sum of SparsePauliOps.

        This is a specialized version of the builtin ``sum`` function for SparsePauliOp
        with smaller overhead.

        Args:
            ops (list[SparsePauliOp]): a list of SparsePauliOps.

        Returns:
            SparsePauliOp: the SparsePauliOp representing the sum of the input list.

        Raises:
            QiskitError: if the input list is empty.
            QiskitError: if the input list includes an object that is not SparsePauliOp.
            QiskitError: if the numbers of qubits of the objects in the input list do not match.
        """
        if len(ops) == 0:
            raise QiskitError("Input list is empty")
        if not all(isinstance(op, SparsePauliOp) for op in ops):
            raise QiskitError(
                "Input list includes an object that is not SparsePauliOp")
        for other in ops[1:]:
            ops[0]._op_shape._validate_add(other._op_shape)

        z = np.vstack([op.paulis.z for op in ops])
        x = np.vstack([op.paulis.x for op in ops])
        phase = np.hstack([op.paulis._phase for op in ops])
        pauli_list = PauliList(BasePauli(z, x, phase))
        coeffs = np.hstack([op.coeffs for op in ops])
        return SparsePauliOp(pauli_list,
                             coeffs,
                             ignore_pauli_phase=True,
                             copy=False)
Exemplo n.º 3
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)
Exemplo n.º 4
0
def pauli_basis(num_qubits, weight=False, pauli_list=False):
    """Return the ordered PauliTable or PauliList for the n-qubit Pauli basis.

    Args:
        num_qubits (int): number of qubits
        weight (bool): if True optionally return the basis sorted by Pauli weight
                       rather than lexicographic order (Default: False)
        pauli_list (bool): if True, the return type becomes PauliList, otherwise PauliTable.

    Returns:
        PauliTable, PauliList: the Paulis for the basis
    """
    if pauli_list:
        pauli_1q = PauliList(["I", "X", "Y", "Z"])
    else:
        warnings.warn(
            "The pauli_basis function with PauliTable output is deprecated as of Qiskit Terra "
            "0.19.0 and will be removed no sooner than 3 months after the releasedate. "
            "Use PauliList by pauli_list=True instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        pauli_1q = PauliTable(
            np.array(
                [[False, False], [True, False], [True, True], [False, True]],
                dtype=bool))
    if num_qubits == 1:
        return pauli_1q
    pauli = pauli_1q
    for _ in range(num_qubits - 1):
        pauli = pauli_1q.tensor(pauli)
    if weight:
        return pauli.sort(weight=True)
    return pauli
Exemplo n.º 5
0
def pauli_basis(num_qubits, weight=False, pauli_list=False):
    """Return the ordered PauliTable or PauliList for the n-qubit Pauli basis.

    Args:
        num_qubits (int): number of qubits
        weight (bool): if True optionally return the basis sorted by Pauli weight
                       rather than lexicographic order (Default: False)
        pauli_list (bool): if True, the return type becomes PauliList, otherwise PauliTable.

    Returns:
        PauliTable, PauliList: the Paulis for the basis
    """
    if pauli_list:
        pauli_1q = PauliList(["I", "X", "Y", "Z"])
    else:
        warnings.warn(
            "The return type of 'pauli_basis' will change from PauliTable to PauliList in a "
            "future release of Qiskit Terra.  Returning PauliTable is deprecated as of "
            "Qiskit Terra 0.19, and will be removed in a future release.  To immediately switch "
            "to the new behaviour, pass the keyword argument 'pauli_list=True'.",
            FutureWarning,
            stacklevel=2,
        )
        pauli_1q = PauliTable(
            np.array(
                [[False, False], [True, False], [True, True], [False, True]],
                dtype=bool))
    if num_qubits == 1:
        return pauli_1q
    pauli = pauli_1q
    for _ in range(num_qubits - 1):
        pauli = pauli_1q.tensor(pauli)
    if weight:
        return pauli.sort(weight=True)
    return pauli
Exemplo n.º 6
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)

        if qargs is not None:
            x1, z1 = self.paulis.x[:, qargs], self.paulis.z[:, qargs]
        else:
            x1, z1 = self.paulis.x, self.paulis.z
        x2, z2 = other.paulis.x, other.paulis.z
        num_qubits = other.num_qubits

        # This method is the outer version of `BasePauli.compose`.
        # `x1` and `z1` have shape `(self.size, num_qubits)`.
        # `x2` and `z2` have shape `(other.size, num_qubits)`.
        # `x1[:, no.newaxis]` results in shape `(self.size, 1, num_qubits)`.
        # `ar = ufunc(x1[:, np.newaxis], x2)` will be in shape `(self.size, other.size, num_qubits)`.
        # So, `ar.reshape((-1, num_qubits))` will be in shape `(self.size * other.size, num_qubits)`.
        # Ref: https://numpy.org/doc/stable/user/theory.broadcasting.html

        phase = np.add.outer(self.paulis._phase,
                             other.paulis._phase).reshape(-1)
        if front:
            q = np.logical_and(x1[:, np.newaxis], z2).reshape((-1, num_qubits))
        else:
            q = np.logical_and(z1[:, np.newaxis], x2).reshape((-1, num_qubits))
        phase = np.mod(phase + 2 * np.sum(q, axis=1), 4)

        x3 = np.logical_xor(x1[:, np.newaxis], x2).reshape((-1, num_qubits))
        z3 = np.logical_xor(z1[:, np.newaxis], z2).reshape((-1, num_qubits))

        if qargs is None:
            pauli_list = PauliList(BasePauli(z3, x3, phase))
        else:
            x4 = np.repeat(self.paulis.x, other.size, axis=0)
            z4 = np.repeat(self.paulis.z, other.size, axis=0)
            x4[:, qargs] = x3
            z4[:, qargs] = z3
            pauli_list = PauliList(BasePauli(z4, x4, phase))

        coeffs = np.kron(self.coeffs, other.coeffs)
        return SparsePauliOp(pauli_list, coeffs)
Exemplo n.º 7
0
    def from_sparse_list(obj, num_qubits):
        """Construct from a list of local Pauli strings and coefficients.

        Each list element is a 3-tuple of a local Pauli string, indices where to apply it,
        and a coefficient.

        For example, the 5-qubit Hamiltonian

        .. math::

            H = Z_1 X_4 + 2 Y_0 Y_3

        can be constructed as

        .. code-block:: python

            # via triples and local Paulis with indices
            op = SparsePauliOp.from_sparse_list([("ZX", [1, 4], 1), ("YY", [0, 3], 2)], num_qubits=5)

            # equals the following construction from "dense" Paulis
            op = SparsePauliOp.from_list([("XIIZI", 1), ("IYIIY", 2)])

        Args:
            obj (Iterable[Tuple[str, List[int], complex]]): The list 3-tuples specifying the Paulis.
            num_qubits (int): The number of qubits of the operator.

        Returns:
            SparsePauliOp: The SparsePauliOp representation of the Pauli terms.

        Raises:
            QiskitError: If the list of Paulis is empty.
            QiskitError: If the number of qubits is incompatible with the indices of the Pauli terms.
        """
        obj = list(obj)  # To convert zip or other iterable

        size = len(obj)  # number of Pauli terms
        if size == 0:
            raise QiskitError("Input Pauli list is empty.")

        coeffs = np.zeros(size, dtype=complex)
        labels = np.zeros(size, dtype=f"<U{num_qubits}")

        for i, (paulis, indices, coeff) in enumerate(obj):
            # construct the full label based off the non-trivial Paulis and indices
            label = ["I"] * num_qubits
            for pauli, index in zip(paulis, indices):
                if index >= num_qubits:
                    raise QiskitError(
                        f"The number of qubits ({num_qubits}) is smaller than a required index {index}."
                    )
                label[~index] = pauli

            labels[i] = "".join(label)
            coeffs[i] = coeff

        paulis = PauliList(labels)
        return SparsePauliOp(paulis, coeffs, copy=False)
Exemplo n.º 8
0
    def _evolve_clifford(self, other, qargs=None, frame="h"):
        """Heisenberg picture evolution of a Pauli by a Clifford."""
        if qargs is None:
            idx = slice(None)
            num_act = self.num_qubits
        else:
            idx = list(qargs)
            num_act = len(idx)

        # Set return to I on qargs
        ret = self.copy()
        ret._x[:, idx] = False
        ret._z[:, idx] = False

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

        # Get action of Pauli's from Clifford
        if frame == "s":
            adj = other.copy()
        else:
            adj = other.adjoint()
        pauli_list = []
        for z in self._z[:, idx]:
            pauli = Pauli("I" * num_act)
            for row in adj.stabilizer[z]:
                pauli.compose(Pauli((row.Z[0], row.X[0], 2 * row.phase[0])),
                              inplace=True)
            pauli_list.append(pauli)
        ret.dot(PauliList(pauli_list), qargs=qargs, inplace=True)

        pauli_list = []
        for x in self._x[:, idx]:
            pauli = Pauli("I" * num_act)
            for row in adj.destabilizer[x]:
                pauli.compose(Pauli((row.Z[0], row.X[0], 2 * row.phase[0])),
                              inplace=True)
            pauli_list.append(pauli)
        ret.dot(PauliList(pauli_list), qargs=qargs, inplace=True)
        return ret
Exemplo n.º 9
0
 def from_list(obj):
     """Construct from a list [(pauli_str, coeffs)]"""
     obj = list(obj)  # To convert zip or other iterable
     num_qubits = len(obj[0][0])
     size = len(obj)
     coeffs = np.zeros(size, dtype=complex)
     labels = np.zeros(size, dtype=f"<U{num_qubits}")
     for i, item in enumerate(obj):
         labels[i] = item[0]
         coeffs[i] = item[1]
     paulis = PauliList(labels)
     return SparsePauliOp(paulis, coeffs)
Exemplo n.º 10
0
    def from_list(obj):
        """Construct from a list of Pauli strings and coefficients.

        For example, the 5-qubit Hamiltonian

        .. math::

            H = Z_1 X_4 + 2 Y_0 Y_3

        can be constructed as

        .. code-block:: python

            # via tuples and the full Pauli string
            op = SparsePauliOp.from_list([("XIIZI", 1), ("IYIIY", 2)])

        Args:
            obj (Iterable[Tuple[str, complex]]): The list of 2-tuples specifying the Pauli terms.

        Returns:
            SparsePauliOp: The SparsePauliOp representation of the Pauli terms.

        Raises:
            QiskitError: If the list of Paulis is empty.
        """
        obj = list(obj)  # To convert zip or other iterable

        size = len(obj)  # number of Pauli terms
        if size == 0:
            raise QiskitError("Input Pauli list is empty.")

        # determine the number of qubits
        num_qubits = len(obj[0][0])

        coeffs = np.zeros(size, dtype=complex)
        labels = np.zeros(size, dtype=f"<U{num_qubits}")
        for i, item in enumerate(obj):
            labels[i] = item[0]
            coeffs[i] = item[1]

        paulis = PauliList(labels)
        return SparsePauliOp(paulis, coeffs, copy=False)
Exemplo n.º 11
0
    def __init__(self,
                 data,
                 coeffs=None,
                 *,
                 ignore_pauli_phase=False,
                 copy=True):
        """Initialize an operator object.

        Args:
            data (PauliList or SparsePauliOp or PauliTable or Pauli or list or str): Pauli list of
                terms.  A list of Pauli strings or a Pauli string is also allowed.
            coeffs (np.ndarray): complex coefficients for Pauli terms.

                .. note::

                    If ``data`` is a :obj:`~SparsePauliOp` and ``coeffs`` is not ``None``, the value
                    of the ``SparsePauliOp.coeffs`` will be ignored, and only the passed keyword
                    argument ``coeffs`` will be used.

            ignore_pauli_phase (bool): if true, any ``phase`` component of a given :obj:`~PauliList`
                will be assumed to be zero.  This is more efficient in cases where a
                :obj:`~PauliList` has been constructed purely for this object, and it is already
                known that the phases in the ZX-convention are zero.  It only makes sense to pass
                this option when giving :obj:`~PauliList` data.  (Default: False)
            copy (bool): copy the input data if True, otherwise assign it directly, if possible.
                (Default: True)

        Raises:
            QiskitError: If the input data or coeffs are invalid.
        """
        if ignore_pauli_phase and not isinstance(data, PauliList):
            raise QiskitError(
                "ignore_pauli_list=True is only valid with PauliList data")

        if isinstance(data, SparsePauliOp):
            if coeffs is None:
                coeffs = data.coeffs
            data = data._pauli_list
            # `SparsePauliOp._pauli_list` is already compatible with the internal ZX-phase
            # convention.  See `BasePauli._from_array` for the internal ZX-phase convention.
            ignore_pauli_phase = True

        pauli_list = PauliList(
            data.copy() if copy and hasattr(data, "copy") else data)

        if coeffs is None:
            coeffs = np.ones(pauli_list.size, dtype=complex)
        else:
            coeffs = np.array(coeffs, copy=copy, dtype=complex)

        if ignore_pauli_phase:
            # Fast path used in copy operations, where the phase of the PauliList is already known
            # to be zero.  This path only works if the input data is compatible with the internal
            # ZX-phase convention.
            self._pauli_list = pauli_list
            self._coeffs = coeffs
        else:
            # move the phase of `pauli_list` to `self._coeffs`
            phase = pauli_list._phase
            count_y = pauli_list._count_y()
            self._coeffs = np.asarray((-1j)**(phase - count_y) * coeffs,
                                      dtype=complex)
            pauli_list._phase = np.mod(count_y, 4)
            self._pauli_list = pauli_list

        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)
Exemplo n.º 12
0
 def paulis(self, value):
     if not isinstance(value, PauliList):
         value = PauliList(value)
     self._pauli_list = value
Exemplo n.º 13
0
 def table(self, value):
     if not isinstance(value, PauliTable):
         value = PauliTable(value)
     self._pauli_list = PauliList(value)